模板静态变量 [英] Template static variable

查看:109
本文介绍了模板静态变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不明白,为什么如果我们在头中定义通常(非模板)类的静态变量,我们有链接器错误,但是在模板的情况下,所有工作正常,此外,我们将有单个静态变量的实例所有翻译单位:



它是模板标题(template.h):

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

template< typename T> Templ< T> :: templStatic = 0;

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

  // unit1.cpp 
#includetemplate.h

int method1(){
return Templ< void> ; :: templStatic ++;
}

此处为第二单位(unit2.cpp):

  // unit2.cpp 
#includetemplate.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;
}

编译,链接和执行此代码后, / p>

  0 
1

$ b b

那么,为什么在模板的情况下,所有的工作正常(和预期)?如何编译器或链接器处理这个(我们可以编译每个.cpp文件在分离的编译器调用,然后链接他们与caling链接器,所以编译器和链接器不看到所有.cpp文件在同一时间)?

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

解决方案

p>这是因为静态数据成员的定义本身就是一个模板。允许这是必要的,因为同样的原因,你被允许有一个功能模板在一个程序中没有内联多次。您需要使用模板来生成结果实体(例如,一个函数或一个静态数据成员)。如果您不允许放置静态数据成员的定义,您将如何实例化以下



  template< typename T> 
struct F {
static int const value;
};

template< typename T>
int const F< T> :: value = sizeof(T);

不知道 T 是 - 标准说,类模板之外的定义是模板定义,其中参数从其类模板所有者继承。






我对GCC做了一些实验。在下面,我们有一个隐式的实例化 F ,以及 F< char> / code>,它必须在.cpp文件中定义,以便在包含多次时不会导致重复的符号错误。

  //翻译单位1 
template< typename T>
struct F {
static int value;
};

template< typename T>
int F< T> :: value = sizeof(T);

//这将属于.cpp文件
模板<> int F< char> :: value = 2;

//这隐含地实例化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#=> 
#切换到重要的
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全局构造函数键入_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 是一个弱符号,这意味着链接器可以在链接时看到其中的多个。 test main F< char> :: value 是全局(非弱)符号。将 main1.o main2.o 链接在一起,我们在地图输出( Wl,-M )以下

 #(错误名称)
。 _ZN1FIfE5valueE
0x080497ac 0x4 main1.o
0x080497ac F< float> :: value

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


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

It's template header (template.h):

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

template<typename T> Templ<T>::templStatic = 0;

It's first unit using template (unit1.cpp)

// unit1.cpp
#include "template.h"

int method1() {
  return Templ<void>::templStatic++;
}

Second unit 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 compilling, linking and executing this code, we will have following output:

0
1

So, why in case of templates all works fine (and as expected) ? How compiler or linker handle this (we can compile each .cpp file in separated calling of compiler, and then link them with caling to linker, so compiler and linker don't "see" all .cpp files at same time) ?

PS: My compiler: msvcpp 9 (but checked on mingw too)

解决方案

It's because the definition of the static data member is itself a template. Allowing this is necessary for the same reason you are allowed to have a function template that's not inline multiple times in a program. You need the template to generate the resulting entity (say, a function, or a static data member). If you wouldn't be allowed to put the definition of a static data member, how would you instantiate the following

template<typename T>
struct F {
  static int const value;
};

template<typename T>
int const F<T>::value = sizeof(T);

It's not known what T is - the Standard says the definition outside the class template is a template definition, in which the parameters are inherited from its class template owner.


I've made some experiment with GCC. In the following, we have one implicit instantiation of F<float>::value, and one explicit specialization of F<char>::value which has to be defined in a .cpp file to not cause duplicated symbol errors when included multiple 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 just another implicit instantiation of the same static data member

template<typename T>
struct F {
  static int value; 
};

template<typename T>
int F<T>::value = sizeof(T);

int test1 = F<float>::value;

Here is what we get with GCC - it makes each implicit instantiation into a weak symbols and sticks it into its own section here. Weak symbols will not cause errors when there exist multiple of them at link time. Instead, the linker will choose one instance, and discards the other ones assuming all of them are 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 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

So as we can see F<float>::value is a weak symbol which means the linker can see multiple of these at link time. test, main and F<char>::value are global (non-weak) symbols. Linking main1.o and main2.o together, we see in the map output (-Wl,-M) the following

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

This indicates that actually it drops all except one instance.

这篇关于模板静态变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆