模板静态variables

我无法理解,为什么如果我们在头文件中定义通常(非模板)类的静态variables,我们有链接器错误,但在模板的情况下工作正常,而且所有的翻译单元之间将有单个静态variables的实例:

它是模板头(template.h):

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

这是使用模板的第一个单元(unit1.cpp)

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

第二单元在这里(unit2.cpp):

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

最后,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; } 

在编译,链接和执行这个代码之后,我们将有如下输出:

 0 1 

那么,为什么在模板的情况下工作正常(和预期的一样)? 编译器或链接器如何处理这个(我们可以在编译器分开的调用中编译每个.cpp文件,然后将它们连接到链接器,从而编译器和链接器不会同时“看到”所有.cpp文件)?

PS:我的编译器:msvcpp 9(但在mingw上也检查过)

这是因为静态数据成员的定义本身就是一个模板。 由于同样的原因,允许这样做是必要的,因为您可以在程序中使用多次内联的函数模板。 你需要模板来生成结果实体(比如一个函数,或者一个静态数据成员)。 如果你不能把静态数据成员的定义,你将如何实例化以下内容

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

不知道T是什么 – 标准说,类模板之外的定义是一个模板定义,其中参数是从它的类模板所有者inheritance的。


我已经做了一些GCC的实验。 在下面的例子中,我们有一个F<float>::value隐式实例,还有一个显式的F<char>::value特殊化,它必须在.cpp文件中定义,当多次包含时不会导致重复的符号错误。

 // 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() { } 

第二个翻译单元只包含相同静态数据成员的另一个隐式实例

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

这里是我们用GCC得到的 – 它使每个隐式的实例化成一个弱符号,并将其粘贴到它自己的部分。 当链接时存在多个符号时,弱符号不会导致错误。 相反,链接器将select一个实例,并假设所有这些实例都是相同的,则放弃其他实例

 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 

所以我们可以看到F<float>::value是一个弱符号,这意味着链接器可以在链接时看到多个这样的符号。 testmainF<char>::value是全局(非弱)符号。 将main1.omain2.o链接在一起,我们在地图输出( -Wl,-M )中看到以下内容

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

这表示实际上除了一个实例外,它全部丢弃。

有解决scheme,你可以创build一个父类,并将其中的静态variables,然后让你的模板类私下inheritance,这里是一个例子:

 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; } 

输出将是:

 Object 1 key is: 0 Object 2 key is: 1