我知道,关于静态(constexpr)成员的链接有很多回答的问题。
但我想知道,为什么使用模板类的外行定义在头文件中工作,但不适用于专门的类。
a)这没有链接器错误:
template<typename, typename>
struct Foobar;
template<typename T>
struct Foobar<int, T> {
static constexpr std::array<int, 1> a = 0000000000000000 w O .rodata._Z6FoobarIiiE1aE 0000000000000004 _Z6FoobarIiiE1aE
;
};
template<typename T>
constexpr std::array<int, 1> Foobar<int, T>::a;
// foo.cpp
std::cout << Foobar<int, int>::a[0] << "\n";
// bar.cpp
std::cout << Foobar<int, int>::a[0] << "\n";
objdump:
foo.o:0000000000000000 w O .rodata._Z6FoobarIiiE1aE 0000000000000004 _Z6FoobarIiiE1aE
bar.o:0000000000475a30 w O .rodata 0000000000000004 _Z6FoobarIiiE1aE
链接文件:template<typename>
struct Foobar;
template<>
struct Foobar<int> {
static constexpr std::array<int, 1> a =
0000000000000100 g O .rodata 0000000000000004 _Z6FoobarIiE1aE
;
};
constexpr std::array<int, 1> Foobar<int>::a;
// foo.cpp
std::cout << Foobar<int>::a[0] << "\n";
// bar.cpp
std::cout << Foobar<int>::a[0] << "\n";
b)这不是(多重定义):
0000000000000420 g O .rodata 0000000000000004 _Z6FoobarIiE1aE
objdump:
foo.o {{1}}
bar.o:{{1}}
我们看到,外部定义在目标文件中有不同的地址(例子b))。
我向你提问:
提前谢谢!
答案 0 :(得分:3)
见[basic.def.odr] / 6:
类类型(第9条),枚举类型(7.2),内联函数可以有多个定义 外部链接(7.1.2),类模板(第14章),非静态函数模板(14.5.6),静态数据成员 类模板(14.5.1.3),类模板的成员函数(14.5.1.1)或模板特化 其中一些模板参数未指定(14.7,14.5.5),在程序中提供了每个定义 出现在不同的翻译单元中,并且定义满足以下要求。 ...
此规则的效果是每个模板声明的行为都像是内联的。 (但它没有扩展到显式实例化和显式特化声明。)
在第一个代码段中,您有
template<typename T>
constexpr std::array<int, 1> Foobar<int, T>::a;
这是模板声明,因此可以进行多重定义。在第二个片段中,您有
constexpr std::array<int, 1> Foobar<int>::a;
这不是模板声明:定义本身不是模板化的,即使被定义的东西恰好是模板的特化。
我向你提问:
- 使用模板技巧是否可以保存?有什么缺点?
醇>
没有&#34;技巧&#34;这里。如果要为 all Foo<T>
定义成员,那么您别无选择,只能将定义放在头文件中。如果要为一个特定的 Foo<T>
定义成员,例如Foo<int>
,那么你不能将定义放在头文件中(直到C ++ 17,它引入了内联变量。)没有技巧,因为你应该做的事情取决于你的具体目标。
(你的第二个问题已在评论部分回答。)