Static Template Variable

I can’t understand why, if we define a static variable of a regular (non-template) class in the header, we have a linker error, but in the case of templates everything works fine, and moreover, we will have one instance of a static variable among all translation units:

Template title (template.h):

// template.h template<typename T> class Templ { public: static int templStatic; }; template<typename T> Templ<T>::templStatic = 0; 

The first block using the template (unit1.cpp)

 // unit1.cpp #include "template.h" int method1() { return Templ<void>::templStatic++; } 

The second unit is here (unit2.cpp):

 // unit2.cpp #include "template.h" int method2() { return Templ<void>::templStatic++; } 

And finally, main.cpp:

 // main.cpp #include <iostream> int method1(); int method2(); int main(int argc, char** argv) { std::cout << method1() << std::endl; std::cout << method2() << std::endl; } 

After compiling, linking and executing this code, we will have the following output:

 0 1 

So why in the case of templates everything works fine (and as expected)? How to deal with the compiler or linker (we can compile each .cpp file in a separate compiler call and then link them using caling to linker, so the compiler and linker do not "see" all .cpp files at the same time)?

PS: My compiler: msvcpp 9 (but also tested on mingw)

+47
c ++ static templates
12 Oct '09 at 10:39
source share
2 answers

This is because the definition of a static data item is the template itself. Allowing this is necessary for the same reason that you are allowed to have a function template that is not embedded several times in the program. You need a template to create the resulting object (say, a function or a static data member). If you fail to establish a definition for a static data item, how would you create an instance of the following

 template<typename T> struct F { static int const value; }; template<typename T> int const F<T>::value = sizeof(T); 

It is not known what T means - the standard says that a definition outside the class template is a template definition in which parameters are inherited from the owner of the class template.




I did an experiment with GCC. In the following case, we have one implicit instantiation of F<float>::value and one explicit specialization F<char>::value , which must be defined in the .cpp file so as not to lead to duplicate symbol errors when included several times.

 // Translation Unit 1 template<typename T> struct F { static int value; }; template<typename T> int F<T>::value = sizeof(T); // this would belong into a .cpp file template<> int F<char>::value = 2; // this implicitly instantiates F<float>::value int test = F<float>::value; int main() { } 

The second translation unit contains only another implicit creation of the same static data item

 template<typename T> struct F { static int value; }; template<typename T> int F<T>::value = sizeof(T); int test1 = F<float>::value; 

Here's what we get with GCC - it makes every implicit creation in weak characters and inserts it into its section. Weak characters will not cause errors if there are several during connection. Instead, the linker selects one instance and discards the others, assuming that they are all the same.

 objdump -Ct main1.o # => # cut down to the important ones 00000000 l df *ABS* 00000000 main1.cpp 0000000a l F .text 0000001e __static_initialization_and_destruction_0(int, int) 00000000 ld .data._ZN1FIfE5valueE 00000000 .data._ZN1FIfE5valueE 00000028 l F .text 0000001c global constructors keyed to _ZN1FIcE5valueE 00000000 g O .data 00000004 F<char>::value 00000000 g O .bss 00000004 test 00000000 g F .text 0000000a main 00000000 w O .data._ZN1FIfE5valueE 00000004 F<float>::value 

Thus, as we can see, F<float>::value is a weak character, which means that the linker can see several of them during communication. test , main and F<char>::value are global (not weak) characters. By linking main1.o and main2.o together, we see on the output of the map ( -Wl,-M ) the following

 # (mangled name) .data._ZN1FIfE5valueE 0x080497ac 0x4 main1.o 0x080497ac F<float>::value 

This means that it actually deletes all but one instance.

+54
Oct 12 '09 at 10:47
source share

There is a solution, you can create a parent class and put a static variable in it, and then make your template class inherit it privately, here is an example:

 class Parent { protected: static long count; }; long Parent::count = 0; template<typename T> class TemplateClass: private Parent { private: int mKey; public: TemplateClass():mKey(count++){} long getKey(){return mKey;} } int main() { TemplateClass<int> obj1; TemplateClass<double> obj2; std::cout<<"Object 1 key is: "<<obj1.getKey()<<std::endl; std::cout<<"Object 2 key is: "<<obj2.getKey()<<std::endl; return 0; } 

The output will be:

 Object 1 key is: 0 Object 2 key is: 1 
+1
Dec 18 '16 at 16:00
source share



All Articles