模板class的静态变量初始化,c++

Template class's static variable initialization, c++

考虑以下代码:

//header.h
template<class T>
class A
{
    static int x;
};

template<class T>
int A<T>::x = 0;

//source1.cpp
#include "header.h"
void f(){} // dummy function


//main.cpp
#include "header.h"
int main(){}

在这种情况下,代码可以完美无误地编译,但是如果我从 class

中删除模板限定符
class A
{
    static int x;
};

int A::x = 0;
  1. 在这种情况下,编译器因 x 的多重定义而出错。谁能解释这种行为?
  2. 而当模板class的静态变量被初始化/实例化时?

顾名思义,模板是将针对不同参数多次使用的代码片段。对于模板,编译器将确保它们的方法和静态字段定义是否仅链接。因此,如果您使用其默认值创建静态字段,编译器将不得不提供单个内存单元(对于相同的模板参数集),即使模板 class header 被多次包含在内也是如此。不幸的是non-template class你需要自己管理。

关于第二个问题,我相信标准并没有说明什么时候需要初始化静态字段,每个编译器都可以按照自己的方式实现。

  1. 有必要 instantiate/initialize cpp 文件中的静态成员而不是 headers。静态成员是 class 的 属性 而不是 objects 的 属性,所以如果你在更多的 cpp 文件中包含 header 文件,看起来你正在更多地初始化它次.

  2. 这个问题的答案比较复杂。模板不是一个 class。它正在按需实例化。这意味着模板的每一次不同使用都是一个独立的 "template instance"。例如,如果您使用 A<int>A<float>,那么您将有 2 个不同的 class,因此您需要初始化 A<int>::xA<float>::x

有关详细信息,请参阅此答案:https://whosebug.com/a/607335/1280316

A class(无论是否为模板)可以(并且应该)在引用它的任何编译单元中声明。

静态字段初始化确实定义了一个变量,因此它应该只存在于一个编译单元中 -> 这就是当 class A 不是模板时出现错误的原因.

但是当你声明一个模板时,在你实例化它之前并没有真正创建任何东西。由于您从未实例化模板,因此从未定义静态字段,因此您不会收到任何错误。

如果您在 source1.cpp(例如 A<B>)和 main.cpp(例如 A<C>)中有两个不同的实例化,一切仍然会很好:您会得到 [= source1 中的 15=] 和 main 中的 A<C>::x => 两个不同的变量,因为 A<B>A<C> 不同 classes.

在不同的编译单元中实例化相同 class 的情况比较棘手。它应该产生一个错误,但如果它产生了,你就很难在模板中声明特殊字段。因此它被编译器作为一种特殊情况进行处理,正如本 other answer 中所解释的那样,不会产生任何错误。

编译器将自行删除重复的模板实例化。如果您将模板 class 转换为常规模板,那么您有责任确保只存在一个静态变量定义(否则将出现链接器错误)。还要记住,静态数据成员不会在不同类型的模板实例化之间共享。使用 c++11,您可以使用外部模板自行控制实例化:using extern template (C++11).

关于静态成员的实例化点:

14.6.4.1 Point of instantiation [temp.point] 1 For a function template specialization, a member function template specialization, or a specialization for a member function or static data member of a class template, if the specialization is implicitly instantiated because it is referenced from within another template specialization and the context from which it is referenced depends on a template parameter, the point of instantiation of the specialization is the point of instantiation of the enclosing specialization. Otherwise, the point of instantiation for such a specialization immediately follows the namespace scope declaration or definition that refers to the specialization.

所以实例化点应该是 ie。如果您是第一次在 main() 中使用您的类型,则紧接在 main() 之后。