[英]Initialize static constexpr member variable of class template
[英]ODR of template class with static constexpr member
我知道,关于静态(constexpr)成员的链接有很多回答的问题。
但我想知道,为什么使用模板类的外部定义在头文件中工作,但不适用于专门的类。
a)这没有链接器错误:
template<typename, typename>
struct Foobar;
template<typename T>
struct Foobar<int, T> {
static constexpr std::array<int, 1> a = {{1}};
};
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: 0000000000000000 w O .rodata._Z6FoobarIiiE1aE 0000000000000004 _Z6FoobarIiiE1aE
链接文件: 0000000000475a30 w O .rodata 0000000000000004 _Z6FoobarIiiE1aE
b)这不是(多重定义):
template<typename>
struct Foobar;
template<>
struct Foobar<int> {
static constexpr std::array<int, 1> a = {{1}};
};
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";
objdump:
foo.o 0000000000000100 g O .rodata 0000000000000004 _Z6FoobarIiE1aE
bar.o: 0000000000000420 g O .rodata 0000000000000004 _Z6FoobarIiE1aE
我们看到,外部定义在目标文件中有不同的地址(例子b))。
我向你提问:
先感谢您!
见[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;
这不是模板声明 :定义本身不是模板化的,即使被定义的东西恰好是模板的特化。
我向你提问:
- 是否保存使用模板技巧? 有什么缺点?
这里没有“诡计”。 如果要为所有 Foo<T>
定义成员,那么您别无选择,只能将定义放在头文件中。 如果要为一个特定的 Foo<T>
定义成员,例如Foo<int>
,则不能将该定义放在头文件中(直到C ++ 17,它引入了内联变量。)没有技巧,因为你应该做什么取决于你的具体目标。
(你的第二个问题在评论部分得到了解答。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.