类模板中的静态成员变量
static member variable in class template
当你有一个包含静态成员的(非模板化)类时,例如:
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;
那么,如果类模板Foo
是在foo.hpp
中定义的,并且translation_unit1.cpp
和translation_unit2.cpp
都包含foo.hpp
,那么对于模板类Foo的某些实例化(例如Foo<int>::x
)的Foo<T>::x
的内存地址对于每个翻译单元会不同吗?
这很好,在这种情况下,在标头中定义它,编译器将确保只有一个实例,如果我们看到标准部分C++草案3.2
一个定义规则第6段说(强调我的):
类类型(子句 9)、枚举类型 (7.2)、具有外部链接的内联函数 (7.1.2)、类模板(子句 14)、非静态函数模板 (14.5.6)、类模板的静态数据成员 (14.5.1.3)、成员函数可以有多个定义
然后我们可以转到第 14.5.1.3
部分 类模板的静态数据成员第 1 段说:
可以在包含静态成员类模板定义的命名空间作用域中提供静态数据成员的定义。
并提供以下示例:
template<class T> class X {
static T s;
};
template<class T> T X<T>::s = 0;
您的问题在 C++11 标准中由 14.4 完美回答:
模板名称具有链接 (3.5)。非成员函数模板可以具有内部链接;任何其他模板名称都应具有外部链接...
因此,类模板将始终具有外部链接,其静态数据成员(常量或非常量)也是如此。因此,无论此表达式出现在哪个翻译单元中,Foo<int>::x
都将始终引用内存中的同一实体。链接器使这发生。
[basic.def.odr]/6 在某些情况下明确允许对"类模板的静态数据成员"(以及其他例外)进行多个定义。
然后,对于这样的实体,它继续D
:
因此,在一个 TU 中实例化的模板的静态数据成员的地址与如果
D
的定义满足所有这些要求,那么程序的行为就好像有一个单一的D
定义一样。如果D
的定义不满足 这些要求,则行为是未定义的。
在另一个 TU 中实例化的同一静态数据成员的地址相等。
完整的引用,我上面提到的段落我强调:
类类型、枚举类型、具有外部链接的内联函数、类模板、非静态函数模板、静态数据成员可以有多个定义 类模板、类模板的成员函数或模板专用化 程序中未指定哪些模板参数,前提是每个定义 出现在不同的翻译单元中,前提是定义满足以下要求。给定这样一个名为
D
在多个翻译单元中定义的实体,则
D
的每个定义应由相同的令牌序列组成;和- 在
D
的每个定义中,根据 3.4 查找的相应名称应指D
定义中定义的实体,或应指代同一实体,在重载解决和部分模板专用化匹配后,除了名称可以引用具有内部链接或没有链接的const
对象,如果该对象在所有定义中具有相同的文字类型D
, 并且对象用常量表达式初始化,并使用对象的值(但不是地址),并且对象在D
的所有定义中具有相同的值;和- 在
D
的每个定义中,相应的实体应具有相同的语言联系;以及- 在每个
D
定义中,所指的重载运算符,对转换函数、构造函数、运算符新函数和运算符删除函数的隐式调用,应指同一函数,或在D
定义中定义的函数;- 在
D
的每个定义中,(隐式或显式)函数调用使用的默认参数被视为其标记序列存在于D
的定义中;也就是说,默认参数受上述三个要求的约束(并且,如果默认参数具有带有默认参数的子表达式,则此要求递归适用)。- 如果
D
是具有隐式声明构造函数的类,则就好像构造函数是在每个使用它的翻译单元中隐式定义的,并且每个翻译单元中的隐式定义应为D
的基类或类成员调用相同的构造函数。如果
D
是模板,并且在多个翻译单元中定义,则上述要求应既适用于模板定义中使用的模板封闭范围的名称,也适用于实例化点的从属名称。如果D
的定义满足所有这些要求,那么程序的行为就好像有一个单一的D
定义一样。如果D
的定义不满足 这些要求,则行为是未定义的。
- C++:是否可以使用非静态成员变量模板?
- 如何在复杂继承中访问静态成员变量
- 静态成员变量不会由 gettext 转换
- 为什么我不能像这样在静态成员函数中调用静态成员变量?
- C++ lambda - 捕获静态成员变量
- 多线程处理中的静态成员变量
- 纯标头库中静态成员变量的正确设置器和 getter
- 如何从具有专用化的类模板定义静态成员变量?
- 为什么可以在没有实例变量的情况下访问静态回调方法中的静态成员变量?
- 声明和定义类静态成员变量不会导致多重声明,这是为什么?
- 为什么不允许静态成员变量的初始化在类中,而允许静态静态成员的初始化
- 类中的静态成员变量和C++中的全局变量有什么区别?
- C++ 静态成员变量 - 文件之间的访问
- 如何在运行时使用静态成员函数初始化静态成员变量
- 模板类中嵌套类的C 静态成员变量
- 如何检查静态成员变量模板?
- 重载运算符和静态成员变量的行为
- 类的静态成员变量(受保护)的作用域
- gcc 和 clang 中 constexpr 静态成员变量的链接器错误
- dll 中静态成员变量的生存期