I have a template class defined in a header file like this. Here I have defined a static variable as well:
#ifndef TEST1_H_
#define TEST1_H_
void f1();
static int count;
template <class T>
class MyClass
{
public:
void f()
{
++count;
}
};
#endif
And I have defined main() function in a different cpp file like this:
int main(int argc, char* argv[])
{
MyClass<int> a;
a.f();
f1();
cout<<"Main:" << count << "\n";
return 0;
}
I have implemented function f1() in a different cpp file like this:
void f1()
{
MyClass<int> a;
a.f();
cout<<"F1: " <<count <<"\n";
}
When I compiled this using VC6, I got the output as "F1:0 Main:2". How is this possible? Also, in general how should I handle if I want to use static variables along with templates?
There is another solution, you can create a shared parent class and put this static variable in it, then make your template class inherit it privately, here's an example:
Output will be:
Putting the static declaration in a header file will cause each .cpp file to get its own version of the variable. So the two cout statements are printing different variables.
Were you expecting "F1:1 Main:1"? You instantiated
MyClass<int>
in two separate translation units (i.e. two object files), and the linker saw that there was a duplicate template instantiation, so it discarded the instantiation that was inf1
's object file.Are you passing
/OPT:ICF
or/OPT:REF
to the VC6 linker? That might be related to the duplicate template instantiation removal (or not; duplicate template instantiations might be a special case, compared to ordinary duplicate functions). GCC seems to do something similar on some platforms.Anyway, I wouldn't rely on this behavior being consistent across compilers. Also, changing the order of object files on the linker command line might affect which instantiation gets discarded.
You're getting two copies of the same variable because you've declared a static variable in a header file. When you declare a global variable
static
this way, you're saying it's local to the compilation unit (the.o
file). Since you include the header in two compilation units, you get two copies ofcount
.I think what you really want here is a static template member variable associated with each instance of the template class. It would look like this:
This will get you a count for each instantiation of your template. That is, you'll have a count for
MyClass<int>
,MyClass<foo>
,MyClass<bar>
, etc.f1()
would now look like this:If you want a count for all instantiations of MyClass (regardless of their template parameters), you do need to use a global variable.
However, you probably don't want a global variable directly because you run the risk of using it before it gets initialized. You can get around this by making a global static method that returns a reference to your count:
Then accessing it from within your class like this:
This will ensure that count gets initialized before it's used, regardless of which compilation unit you access it from. See the C++ FAQ on static initialization order for more details.
I think this is actually undefined behaviour.
According to C++14 [basic.def.odr]/6:
The problem is that in the first
.cpp
file, the namecount
withinf1
refers to a different object than the namecount
withinf1
in the second.cpp
file, thus violating the condition that corresponding names should refer to the same entity.They are different objects because of the
static
specifier which says that each translation unit gets its own object with that name.