模板类静态成员变量应该在.h文件中定义

时间:2011-08-18 14:20:59

标签: c++ templates static member standards

如果模板类定义包含依赖于模板类型的静态成员变量,我不确定可靠行为应该是什么?

在我的情况下,最好将静态成员的定义放在与类定义相同的.h文件中,因为

  1. 我希望这个类对于我目前没有的许多模板数据类型是通用的 知道。
  2. 我只希望共享静态成员的一个实例 在我的程序中为每个给定的模板类型。 (一个适用于所有MyClass<int>,一个适用于所有MyClass<double>等等。
  3. 我可以最简单地说,列出的代码at this link在使用gcc 4.3编译时的行为完全符合我的要求。这种行为是否符合C ++标准,以便在使用其他编译器时可以依赖它?

    该链接不是我的代码,而是由CodeMedic发布到讨论here的反例。我发现了其他几个像one这样的辩论,但我认为没有任何结论。

    我认为链接器正在整合找到的多个定义(在示例a.ob.o中)。 这是必需/可靠的链接器行为吗?

3 个答案:

答案 0 :(得分:20)

来自N3290,14.6:

  

类模板的静态数据成员应在中定义   每个翻译单元都隐式实例化[...],除非相应的专业化被明确地实例化[...]。

通常,您将静态成员定义与模板类定义一起放在头文件中:

template <typename T>
class Foo
{
  static int n;                       // declaration
};

template <typename T> int Foo<T>::n;  // definition

扩展特许权:如果您计划在代码中使用显式实例化,例如:

template <> int Foo<int>::n = 12;

然后你必须将模板化的定义放在标题中如果Foo<int>也用于除包含显式实例化的TU之外的其他TU,那么你就会获得多个定义

但是,如果确实需要为所有可能的参数设置初始值而不使用显式实例化,则必须将其放在标题中,例如:与TMP:

// in the header
template <typename T> int Foo<T>::n = GetInitialValue<T>::value;  // definition + initialization

答案 1 :(得分:2)

这完全是对@Kerrek SB的优秀答案的补充。我将它添加为注释,但已经有很多注释,所以默认情况下会隐藏新注释。

所以,我看到的他和其他例子在某种意义上是“容易的”,因为事先知道静态成员变量的类型。这很容易,因为编译器例如知道任何模板实例化的存储大小,因此可以认为编译器可以使用时髦的修改方案,输出变量定义一次,并将其余部分卸载到链接器,这甚至可以工作。

但是当静态成员类型依赖于模板参数时,它有点令人惊讶。例如,以下作品:

template <typename width = uint32_t>
class Ticks : public ITimer< width, Ticks<width> >
{
protected:
    volatile static width ticks;
}
template <typename width> volatile width Ticks<width>::ticks;

(请注意,静态var的显式实例化不需要(或允许)“width”的默认规范。)

因此,它带来了更多的想法,C ++编译器必须做很多处理 - 特别是,实例化模板,不仅需要模板本身,而且还必须收集所有[静态成员]显式实例化(人们可能只是想知道为什么他们被制作成单独的句法结构,而不是在模板类中拼写出来的东西)。

至于链接器级别的实现,对于GNU binutils,它的“常用符号”: http://sourceware.org/binutils/docs/as/Comm.html#Comm。 (对于Microsoft工具链,它的名字叫做COMDAT,正如另一个答案所说)。

答案 2 :(得分:1)

链接器处理此类情况几乎与应用了__declspec(selectany)声明的非模板类静态成员完全相同,如下所示:

class X {
public:
X(int i){};
};
__declspec(selectany) X x(1);//works in msvc, for gcc use __attribute__((weak))

并且为msdn says:“在链接时,如果看到COMDAT的多个定义,链接器会选择一个并丢弃其余的...对于动态初始化的全局对象,selectany将丢弃未引用的对象的初始化代码,以及。“