首页 > 解决方案 > 模板变量是否允许在多个翻译单元中并有效合并?

问题描述

请参阅以下内容:

https://en.cppreference.com/w/cpp/language/definition#One_Definition_Rule

http://eel.is/c++draft/basic.def.odr#12

它声明类模板的多个定义、类模板的静态数据成员、部分模板特化等是允许的,并将作为一个单一的定义。太好了......但它没有在任何地方提到变量模板?

如果我在多个翻译单元中有以下内容:

template<typename T>
T my_data{};

inline void test() {
    my_data<int> = 1;
}

每个翻译单元将被赋予其自己的my_data导致多个符号的定义,还是将它们全部有效地合并到程序中的单个定义中,其中调用test()一个翻译单元将修改另一个翻译单元的变量?

它在标准中的什么地方提到了这种行为?

标签: c++templates

解决方案


根据 c++14 标准[basic.def]/4

每个程序都应包含在该程序中被丢弃的语句之外的每个非内联函数或变量的确切定义;无需诊断。

因此,如果my_data<T>在多个翻译单元中与相同的模板参数一起使用,您应该有一个 odr-violation(没有任何诊断)。inline变量出现在 c++17 中来解决这个问题,这就是为什么type_traits声明 *_v 系列变量模板的原因inline

在实践中,使用 Gcc 和 Clang(至少,我无法检查其他编译器),您不会遇到任何 odr 违规,因为模板变量具有“模糊链接”(就像它们被声明为内联一样)。

你可以用nm. 如果您运行此命令行g++ -c test.cpp -std=c++14 && nm test.o | c++filt | grep my_data,您应该会看到这是根据文档my_data<int>的类别符号:unm

该符号是唯一的全局符号。这是标准 ELF 符号绑定集的 GNU 扩展。对于这样的符号,动态链接器将确保在整个过程中只有一个具有此名称和类型的符号在使用中。


核心问题 #1849中,人们可以读到这个晦涩的句子:

6.2 [basic.def.odr] 第 6 段中关于何时可以在程序中多次声明实体的描述没有但应该讨论变量模板。

我敢打赌,如果所有编译器都给变量模板一个模糊的链接,那么未来的标准修订版可能会反映这一点。但现在我们应该使用 inline 说明符,因为它在 stl 中完成。


推荐阅读