2013-10-14 211 views
4

当你有一个(非模板化)类,它包含一个静态成员,如:静态成员变量

class Foo 
{ 
    public: 

    static int x; 
}; 

然后Foo::x必须在一个被定义的,仅一个转换单元,或者编译器会抱怨多种定义。因此,在somefile.cpp你必须定义它:

int Foo::x = 10; 

这样,访问Foo::x被访问相同的内存地址的转换单元。

但如果Foo是类模板?现在

template <class T> 
class Foo 
{ 
    public: 

    static int x; 
}; 

Foo<T>::x可以在头文件中定义,说:

template <class T> 
int Foo<T>::x = 10; 

所以,如果类模板Foofoo.hpptranslation_unit1.cpptranslation_unit2.cpp既包括foo.hpp定义,会记忆为模板类Foo的一些实例的Foo<T>::x地址,(如Foo<int>::x例如)对于每个转换单元不同?

+3

只有一次'Foo :: x'的实例,链接器会照顾它。 –

+0

[this](http://stackoverflow.com/questions/19341360/is-static-member-variable-initialized-in-a-template-class-if-the-static-menber-i/19341402#19341402)is不是愚蠢的,但它有一些关于如何静态成员和模板一起工作的标准报价 – aaronman

回答

3

这是完全正常的,在这种情况下,在头编译器将确保定义它的存在只有一个实例,如果我们看到的草案C++标准节3.2一个定义规则款说( 重点矿山):

可以有多于一个类型中的一种定义(第9节),枚举类型(7.2),与外部连接(7.1.2内联函数),类模板(第14条),非静态函数模板(14.5.6),类模板的静态数据成员(14.5.1。3),成员函数

然后我们可以去部分的类模板段落 14.5.1.3静态数据成员表示:

可以提供一种用于将静态数据成员定义一个包含静态成员的类模板定义的命名空间范围。

并提供了以下示例:

template<class T> class X { 
    static T s; 
}; 
template<class T> T X<T>::s = 0; 
+0

“在这种情况下,在头文件中定义它,编译器将确保只有一个实例”我认为,正如Daniel Frey所说,它应该是*链接器*(如果两者有区别);同样,每个TU只允许一个定义。 (好吧,如果在同一个TU中有多个,编译器通过发布诊断消息来确保只有一个定义;) – dyp

1

[basic.def.odr]/6明确允许在某些情况下“一类的模板的静态数据成员”(与其他例外)的多个定义。

它再持续这样的实体D

如果D的定义满足所有这些要求,则程序应表现为,如果有的D一个统一的定义。如果D定义不满足 这些要求,则行为是不确定的。

因此,在一个TU中实例化的模板的静态数据成员的地址与在另一个TU中实例化的相同静态数据成员的地址相等。上面提到强调


完整的报价,通道由我:

可以有一个类类型的多个定义,枚举类型,具有外部链接,类模板内联函数,非类模板的静态数据成员 ,类模板的成员函数或模板专用 某些模板参数未在程序中指定,前提是每个定义 出现在不同的transl并且提供的定义满足以下要求。鉴于这样的实体命名D在多于一个翻译单元,然后

  • D每个定义应包括令牌的相同序列的定义;
  • D的每个定义中,根据3.4查找到的对应名称应指的是在D的定义中定义的实体,或者应该指在重载分辨率和部分模板专业化匹配后的同一实体,不同之处在于一个名称可以指与内部或无键的const对象,如果该对象具有在D所有定义相同的文本类型,并且该目的是利用一个常量表达式初始化,和的值(但不是地址)对象被使用,并且该对象在D的所有定义中具有相同的值;和
  • D每个定义
  • ,相应的实体应具有相同的语言联动;和
  • D的每个定义中,所涉及的重载操作符,对转换函数,构造函数,操作符新函数和操作符删除函数的隐式调用应指代相同的函数,或涉及D定义中定义的函数;和
  • D每个定义
  • ,由(隐式或显式的)函数调用使用的默认参数被视为如果令牌序列存在于的D定义;即,默认参数是受上述三个要求(和,如果默认参数具有默认参数的子表达式,这一要求递归地应用)。
  • 如果D是一个具有隐式声明的构造函数的类,就好像构造函数是在每个使用odr的翻译单元中隐式定义的,并且每个翻译单元中的隐式定义都应该调用相同的构造函数基类或D的类成员。

如果D是一个模板,在多于一个翻译单元被定义,则上述要求应当向从模板的封闭范围在模板定义中所用的名称在点应用,并且还依赖名实例。 如果D的定义满足所有这些要求,则程序应表现为,如果有的D一个统一的定义。如果D定义不满足 这些要求,则行为是不确定的。

1

你的问题是完全由在C++ 11标准14.4回答:

模板名称具有连杆(3.5)。非成员函数模板可以具有内部链接;任何其他模板名称应具有外部链接...

因此,类模板总会有外部链接等都是它的静态数据成员(常量或没有)。因此,Foo<int>::x将始终引用内存中的同一个实体,而不管该表达式出现在哪个翻译单元中。链接程序可以实现此目的。