In some code I am reviewing I ran into a case where Clang and Gcc disagree. After looking around awhile I cant seem to figure out who is right.
Disclaimer: I know there is a better Singleton pattern, but this is the one used in the code.
Notes:
gcc 7.4.0 on Ubuntu (No error)
clang 6.0.0 on Ubuntu (Throws error)
Difference appears to exist for all post C++11 ISO versions, but I did not try earlier.
foo.hh
#include "sing.hh"
class Foo {
public:
Foo();
~Foo();
static Foo *getSingleton(){
return singleton<Foo>::instance();
}
};
foo.cc
include "foo.hh"
//removing this line results in the error for clang disappearing
template<> singleton<Foo>::GetInstance singleton<Foo>::instance = nullptr;
int main(){};
sing.hh
template<typename T>
class singleton{
typedef T *(*GetInstance)(void);
public:
static GetInstance instance;
};
Results:
$ clang++ foo.cc
foo.cc:3:56: error: explicit specialization of 'instance' after instantiation
template<> singleton<Foo>::GetInstance singleton<Foo>::instance = nullptr;
^
./foo.hh:10:32: note: implicit instantiation first required here
return singleton<Foo>::instance();
^
1 error generated.
$ g++ foo.cc <- No Errors
Neither compiler is technically wrong. The code is invalid, but C++ implementations are not required to give a diagnostic message about this type of error.
Standard [temp.expl.spec]/6 says (emphasis mine):
If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.
You can fix this by declaring the explicit specialization immediately after the definition of singleton in sing.hh:
struct Foo;
template<> singleton<Foo>::GetInstance singleton<Foo>::instance;
Or, if you want all specializations to initialize as null pointers, you can just define the member of the general class template, again probably in sing.hh. Then there's no need for explicit specializations, unless you want a different initializer for some certain type(s).
template<typename T>
typename singleton<T>::GetInstance singleton<T>::instance = nullptr;
From this answer here and the cpp-reference here.
Explicit specialization may be declared in any scope where its primary
template may be defined [...].
Explicit specialization has to appear after the non-specialized
template declaration.
Specialization must be declared before the first use that would cause
implicit instantiation, in every translation unit where such use
occurs
If the explicit specialization was in the sing.cpp file, then neither compiler would complain. Alternatively, you can use forward declaration to do the following and both clang and gcc would be happy.
#include <iostream>
template<typename T>
struct singleton
{
typedef T *(*GetInstance)(void);
static GetInstance instance;
};
template<>
singleton<struct Foo>::GetInstance singleton<struct Foo>::instance = nullptr;
struct Foo
{
static Foo *getSingleton()
{
return singleton<Foo>::instance();
}
};
int main()
{
}
Online code example: https://rextester.com/SPZLS83155
Related
I have some classes which can be checked. The code which implements this declares a function template in a header file and specializes it in different source files:
// check.h
template <class T>
bool check(const T& object);
// class1.h
struct Class1 {int mass;};
// check_class1.cpp
#include "class1.h"
#include "check.h"
template <>
bool check(const Class1& object) {return object.mass < 100;}
// class2.h
struct Class2 {int price;};
// check_class2.cpp
#include "class2.h"
#include "check.h"
template <>
bool check(const Class2& object) {return object.price < 1000;}
// class3.h
struct Class3 {int x;};
... // 10 more classes which I can check
This code is used like this:
#include "class1.h"
#include "class2.h"
#include "class3.h"
#include "check.h"
int main()
{
Class1 object1{50};
Class2 object2{500};
Class3 object3{8};
check(object1); // OK
check(object2); // OK
check(object3); // a link error appears here
}
This works pretty well. When I add another class Class3 which I can check, I don't need to touch the header file, because it defines a very wide interface. If I forgot to implement the check function for Class3, the linker will remind me with an error message.
My question is: is this behavior guaranteed, or does my code work by luck? I am using Visual Studio.
If I want to specialize my function template, shouldn't I declare all my specializations in the header file?
I'd add those declarations to be on the safe side (well, assuming I don't overload instead for whatever reason). I don't think the law is too clear on that. For one, we have
[temp.expl.spec]
6 If a template, a member template or a member of a class
template is explicitly specialized then that specialization shall be
declared before the first use of that specialization that would cause
an implicit instantiation to take place, in every translation unit in
which such a use occurs; no diagnostic is required. If the program
does not provide a definition for an explicit specialization and
either the specialization is used in a way that would cause an
implicit instantiation to take place or the member is a virtual member
function, the program is ill-formed, no diagnostic required. An
implicit instantiation is never generated for an explicit
specialization that is declared but not defined.
Which, if I read correctly, means that if an explicit specialization is added to main.cpp, then it must appear before main. Because that is where an implicit instantiation may occur. The paragraph doesn't make your code flat out ill-formed NDR, because the usage and the explicit specialization appear in different TU. But it does raise concerns.
On the other hand, there is this paragraph:
[temp]
7 A function template, member function of a class template,
variable template, or static data member of a class template shall be
defined in every translation unit in which it is implicitly
instantiated unless the corresponding specialization is explicitly
instantiated in some translation unit; no diagnostic is required.
This one allows us to explicitly instantiate in separate unseen TU's. But it doesn't provide an allowance for explicit specializations. Whether or not that's intentional or an omission I cannot say.
The reason it works is likely due to how the whole thing is implemented. When the function declaration is implicitly instantiated it produces a symbol that just so happens to match the one produced by the explicit specialization. Matching symbols means a happy linker, so everything builds and runs.
But from a language-lawyer perspective, I think we can call the behavior here undefined by omission. It's undefined simply because the standard doesn't address it. So going back to my opening statement, I'd add them to be on the safe side, because at least then the placement is addressed by the standard.
You have to declare each explicit specialization before their use. But you can do that in the headers declaring the types for which it is specialized.
// class2.h
struct Class2 {int price;};
template <class T>
bool check(const T& object);
template <>
bool check(const Class2& object)
(I still don't understand why using overloads is not an option).
I have a template class with a structure about as follows:
foobar.h:
class Bar
{
Bar(float f);
};
template<typename T>
class Foo
{
Foo(const Bar&);
...
static const Foo mkFooConst;
};
typedef Foo<float> MathFoo;
foobar.c:
template<> const MathFoo MathFoo::mkFooConst(Bar(0.815));
Now this seems to be the commonly recommended way to initialize a static member of a template class that can't be initialized trivially in its declaration. And with both VS 2015 and g++ it seems to work nicely.
However, clang doesn't agree, and issues a warning when the class is ultimately used:
warning: instantiation of variable 'Foo<float>::mkFooConst' required
here, but no definition is available [-Wundefined-var-template]
...
note: add an explicit instantiation declaration to suppress this
warning if 'Foo<float>::mkFooConst' is explicitly instantiated in
another translation unit.
It is my understanding that this means I should add the following code to the header file:
extern template class MathFoo<float>;
and presumably the following to the cpp file:
template class MathFoo<float>;
but no matter where I put it, all this gives me is an outright error instead of the warning - either 'explicit specialization after instantiation' or 'explicit specialization after instantiation' 'explicit instantiation after specialization'.
I also tried to add an explicit instantiation declaration for just the member, but that didn't work either.
Any ideas?
C++17 is not an option for me, unfortunately; the code must be compatible with C++11.
You have to put:
template<> const MathFoo MathFoo::mkFooConst;
// Declaration only, mkFooConst{} would be a definition.
Demo with multiple file
No warning with clang
I'm trying to use a static member in a normal member function. But the compiler report some errors. Pls take a look at this code
#include <memory>
template<typename T>
class MyClass {
private:
static std::allocator<T> alloc;
T *p;
public:
void assign(T e) {
p = alloc.allocate(1);
alloc.construct(p, e);
}
};
and this is how I use it:
#include 'myclass.h'
int main() {
MyClass<int> cls;
cls.assign(4);
};
And the compiler give this error:
/Users/liuziqi/CLionProjects/cpplearning/src/tt.h:17:9: warning: instantiation of variable 'MyClass<int>::alloc' required here, but no definition is available [-Wundefined-var-template]
p = alloc.allocate(1);
^
/Users/liuziqi/CLionProjects/cpplearning/src/main.cpp:49:7: note: in instantiation of member function 'MyClass<int>::assign' requested here
cls.assign(4);
^
/Users/liuziqi/CLionProjects/cpplearning/src/tt.h:13:28: note: forward declaration of template entity is here
static std::allocator<T> alloc;
^
/Users/liuziqi/CLionProjects/cpplearning/src/tt.h:17:9: note: add an explicit instantiation declaration to suppress this warning if 'MyClass<int>::alloc' is explicitly instantiated in another translation unit
p = alloc.allocate(1);
I can not figure out which part is wrong.....I have defined that static member and any member function should be able to use it. Is this error relevant to template? (I've just learned template and not sure If I use it correctly.)
I'm going to take a shot at describing what the warning is referring to in addition to answering the "how to fix it" question (which certainly has been answered many times before)...
Because MyClass is a template, the compiler expects all the code for the templated class to be available in the same file (myclass.h). Since you only declare but do not define alloc in myclass.h, the compiler assumes you made a mistake. It's not absolutely required to be there (hence the warning and not error) and you could disable the warning if you define the variable elsewhere, but it's almost certainly just a mistake.
If you're using c++17, the easiest way to deal with this is to declare the static member as inline, so it will be defined right there:
static inline std::allocator<T> alloc;
live: https://godbolt.org/g/cr1aUP
Prior to C++17, you would explicitly define the variable in myclass.h:
template<typename T>
std::allocator<T> MyClass<T>::alloc;
live: https://godbolt.org/g/2Znqst
My network code uses template specialization to serialize types that can not simply be copied. I defined a general template
template<typename T> struct TypeHandler
that handles all types that can be transported by a simple memcpy and then I define specializations for all other types. The problem now is that I have a file with multiple such specializations and if I compile the code with Visual Studio everything works fine. But with gcc all template specializations in that file get used with the exception of
template<> struct TypeHandler<uint32_t>
which variable length encodes the integer to save space.
Namespaces are the same for all TypeHandler versions and they are even in the same file. But for some reason gcc decides to use the generalized version and I don't really know why.
EDIT:
It seems that gcc uses the instantiation of TypeHandler from an other project that this one links against but doesnt have a specialization for uint32_t even so it transmits uint32_t fields. GCC doesnt give me any error though. How can i tell gcc to use the specialization like Visual Studio does ?
EDIT2:
managed to generate an SSCCE http://netload.in/dateiz3R4eTVqi3/src.tar.gz.htm
the bug here is the other way around but well.
EDIT3:
fixed filesize : http://netload.in/dateixP6iOvc6bD/src.zip.htm
Minimized to:
test1.cpp:
#include <iostream>
#include <stdint.h>
template<typename T>
struct TypeHandler
{
void Print() { std::cout << "base" << std::endl; }
};
void test1()
{
std::cout << "p1" << std::endl;
TypeHandler<uint32_t>().Print();
}
test2.cpp:
#include <iostream>
#include <stdint.h>
template<typename T>
struct TypeHandler
{
void Print() { std::cout << "base" << std::endl; }
};
template<>
struct TypeHandler<uint32_t>
{
void Print() { std::cout << "int" << std::endl; }
};
void test2()
{
std::cout << "p2" << std::endl;
TypeHandler<uint32_t>().Print();
}
main.cpp:
void test1();
void test2();
int main(){
test1();
test2();
}
On Windows/MinGW 4.8.2, compiling with g++ test1.cpp test2.cpp main.cpp -o test and running produces
p1
base
p2
base
while using g++ test2.cpp test1.cpp main.cpp -o test produces
p1
int
p2
int
This is a straightforward standard violation causing undefined behavior. You can't explicitly specialize the same template in one translation unit but not the other. The explicit specialization is not visible in test1.cpp, causing the compiler to generate an implicit instantiation from the base template. So you get two TypeHandler<uint32_t> specializations, and in this instance it appears that the linker decided to pick the one from the first object file it saw. From §14.7.3 [temp.expl.spec]/p6 of the standard (emphasis mine):
If a template, a member template or a member of a class template is
explicitly specialized then that specialization shall be declared
before the first use of that specialization that would cause an
implicit instantiation to take place, in every translation unit in
which such a use occurs; no diagnostic is required. If the program
does not provide a definition for an explicit specialization and
either the specialization is used in a way that would cause an
implicit instantiation to take place or the member is a virtual member
function, the program is ill-formed, no diagnostic required. An
implicit instantiation is never generated for an explicit
specialization that is declared but not defined.
Also, obligatory quote of the next paragraph (emphasis mine):
The placement of explicit specialization declarations for function
templates, class templates, member functions of class templates,
static data members of class templates, member classes of class
templates, member enumerations of class templates, member class
templates of class templates, member function templates of class
templates, member functions of member templates of class templates,
member functions of member templates of non-template classes, member
function templates of member classes of class templates, etc., and the
placement of partial specialization declarations of class templates,
member class templates of non-template classes, member class templates
of class templates, etc., can affect whether a program is well-formed
according to the relative positioning of the explicit specialization
declarations and their points of instantiation in the translation unit
as specified above and below. When writing a specialization, be
careful about its location; or to make it compile will be such a trial
as to kindle its self-immolation.
I am using VS Express 2013 trying to compile a c++ project. I've created a template class with some functions. The class and its functions are all in one header file. I've included the file, I've used the class, I've called functions from it, and despite all that visual studio won't compile the classes' functions that I'm not using. I've turned off all optimizations. Do I HAVE to use a function that I've written just to see that it compiles or not?
Here is the function:
void remove(ID id)
{
sdfgsdfg456456456456sfdsdf
}
The function shouldn't compile. And indeed the project won't compile if I do use this function, but if I don't use the function the project will compile, even if I use other functions from within this class.
Is there a fix to this? Will the same thing happen if I implement the function in a .cpp file?
Edit: I neglected to mention it is a template class. I've added that information in.
As revealed in comments, the reason this is happening is because remove() is a function in a class template. The compiler only instantiates template code if it is actually used; if you don't call remove(), it can have all the syntax errors you want and nobody will complain.
More formally, § 14.7.1 of the standard states (emphasis mine):
The implicit instantiation of a class template specialization causes
the implicit instantiation of the declarations, but not the
definitions or default arguments, of the class member functions
And later in the same section:
An implementation shall not implicitly instantiate a function
template, a member template, a non-virtual member function, a member
class, or a static data member of a class template that does not
require instantiation.
(the word "implicit" is key here; if you use explicit template instantiation, the compiler will immediately try to instantiate all members using the indicated type(s) and fail if any doesn't compile)
This is not just an optimization; you can exploit this behavior to instantiate class templates with types that only support a subset of the template's operations. For example, suppose you write a template class that will be used with types that support a bar() operation, and in addition, some will also support baz(). You could do this:
template<typename T>
class Foo
{
private:
T _myT;
public:
void bar()
{
_myT.bar();
}
void baz()
{
_myT.baz();
}
};
Now suppose you have these too:
struct BarAndBaz
{
void bar() {}
void baz() {}
};
struct BarOnly
{
void bar() {}
};
This will compile and run just fine:
void f()
{
Foo<BarAndBaz> foo1;
foo1.bar();
foo1.baz();
Foo<BarOnly> foo2;
foo2.bar();
// don't try foo2.baz()!
// or template class Foo<BarOnly>!
}