【问题标题】:Template class's static variable initialization, c++模板类的静态变量初始化,c++
【发布时间】:2015-04-27 07:24:02
【问题描述】:

考虑以下代码:

//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 A
{
    static int x;
};

int A::x = 0;
  1. 在这种情况下,编译器因 x 的多个定义而出错。任何人都可以解释这种行为吗?
  2. 而当模板类的静态变量被初始化/实例化?

【问题讨论】:

  • 1) 类模板成员仅在需要时实例化。 2) 这是一个棘手的问题。
  • Template static variable 的可能重复项
  • 简而言之,从不在头文件中实例化静态变量,这几乎每次都会带来麻烦。每次包含头文件时,静态都会被实例化。
  • 实际上,当你的类是模板时,编译器会处理多重定义。我的问题是,这个静态是否在不使用此类的情况下初始化和实例化?

标签: c++ templates static instantiation linkage


【解决方案1】:

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

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

14.6.4.1 实例化点 [temp.point] 1 对于函数模板特化、成员函数模板特化或 成员函数或类模板的静态数据成员,如果特化是隐式实例化的 因为它是从另一个模板专业化和引用它的上下文中引用的 取决于模板参数,特化的实例化点是 封闭专业化的实例化。否则,这种特化的实例化点 紧跟在引用特化的命名空间范围声明或定义之后。

所以 instatiation 点应该是 ie。如果你第一次在 main() 中使用你的类型,就在 main() 之后。

【讨论】:

  • 所以当 C++ 标准坚持所有静态数据都应该在 main 不打扰这种情况之前进行初始化时,我是否正确?
  • 模板类的静态成员的构造函数肯定会在main执行之前执行。当代码结构以需要其定义的方式引用模板特化时,就会创建 POI(Point Of Instation)。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:
    1. 必须在 cpp 文件中而不是在头文件中实例化/初始化静态成员。静态成员是类的属性而不是对象的属性,所以如果你在更多的 cpp 文件中包含头文件,看起来你正在初始化它更多次。

    2. 这个问题的答案比较复杂。模板不是一类。它是按需实例化的。这意味着模板的每一种不同用途都是一个独立的“模板实例”。例如,如果您使用 A&lt;int&gt;A&lt;float&gt;,那么您将拥有 2 个不同的类,因此您需要初始化 A&lt;int&gt;::xA&lt;float&gt;::x

    有关更多信息,请参阅此答案:https://stackoverflow.com/a/607335/1280316

    【讨论】:

    • 这不仅是一种好的做法,还是一种要求或 ODR。除非你的标题没有被几个 TU 包含。
    【解决方案4】:

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

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

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

    如果你在source1.cpp(比如A&lt;B&gt;)和main.cpp(比如A&lt;C&gt;)中有两个不同的实例化,一切都会好起来的:你会在source1中得到A&lt;B&gt;::x,在main =中得到A&lt;C&gt;::x > 两个不同的变量,因为A&lt;B&gt;A&lt;C&gt; 是不同的类。

    在不同的编译单元中实例化同一个类的情况比较棘手。它应该会产生一个错误,但如果确实如此,您几乎无法在模板中声明特殊字段。因此,编译器将其作为特殊情况进行处理,正如other answer 中所解释的那样,不会产生错误。

    【讨论】:

      猜你喜欢
      • 2017-05-26
      • 2021-08-07
      • 1970-01-01
      • 2013-07-11
      • 1970-01-01
      • 2011-06-28
      • 1970-01-01
      • 2021-10-21
      • 1970-01-01
      相关资源
      最近更新 更多