模板静态变量

我无法理解,为什么如果我们在头文件中定义了常用(非模板)类的静态变量,我们有链接器错误,但是如果模板全部正常工作,并且我们将在所有翻译单元之间具有单个静态变量实例:

它是模板头(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是什么 - 标准说,类模板之外的定义是一个模板定义,其中参数是从它的类模板所有者继承的。


我已经对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得到的 - 它使每个隐式实例化成一个弱符号,并将其粘贴到它自己的部分。 当链接时存在多个符号时,弱符号不会导致错误。 相反,链接器将选择一个实例,并假定所有这些实例都相同,则丢弃其他实例

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 l    d  .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

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


有解决方案,您可以创建一个父类,并将静态变量放在其中,然后让您的模板类私下继承,下面是一个示例:

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
链接地址: http://www.djcxy.com/p/51557.html

上一篇: Template static variable

下一篇: lambda expression vs static method