具有静态constexpr成员的模板类的ODR
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";
目标转储:
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";
目标转储:
foo.o0000000000000100 g O .rodata 0000000000000004 _Z6FoobarIiE1aE
bar.o:0000000000000420 g O .rodata 0000000000000004 _Z6FoobarIiE1aE
正如我们所看到的,越界的定义在对象文件中有不同的地址(示例b)。
我想问你的问题:
- 使用模板技巧是否保存?缺点是什么
- 在未来像b这样的情况下,放宽odr的定义会有用吗
提前谢谢!
参见[basic.def.odr]/6:
类类型(第9条)、枚举类型(7.2)、内联函数可以有多个定义外部链接(7.1.2)、类模板(第14条)、非静态函数模板(14.5.6)、静态数据成员类模板的成员函数(14.5.1.1),或如果每个定义出现在不同的翻译单元中,前提是定义满足以下要求。。。
此规则的效果是每个模板声明的行为都像是内联的。(但它没有扩展到显式实例化和隐式专门化声明。)
在第一个片段中,您有
template<typename T>
constexpr std::array<int, 1> Foobar<int, T>::a;
这是一个模板声明,因此可以进行多重定义。在第二个片段中,您有
constexpr std::array<int, 1> Foobar<int>::a;
这不是模板声明:定义本身不是模板化的,即使所定义的东西恰好是模板的专门化。
我想问你的问题:
- 使用模板技巧是否保存?缺点是什么
这里没有"窍门"。如果您想为allFoo<T>
定义成员,那么您别无选择,只能将定义放入头文件中。如果你想为一个特定的Foo<T>
(如Foo<int>
)定义成员,那么你不能把定义放在头文件中(直到C++17引入了内联变量。)没有什么诀窍,因为你应该做什么取决于你的特定目标。
(您的第二个问题已在评论部分得到回答。)
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 助记符和指向成员语法的指针
- 用于访问容器<T>数据成员的正确 API
- 内置函数可查看CPP中的成员变量
- 是否可以初始化不可复制类型的成员变量(或基类)
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 嵌套在类中时无法设置成员数据
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 将函数类成员映射到类本身内部
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 将Ref对象作为类成员
- 为什么调用成员函数不调用该对象的 ODR-USE?
- 具有静态constexpr成员的模板类的ODR
- 为什么静态成员的类内初始化会违反 ODR
- 是否应该定义静态constexpr类成员变量,即使它们不是ODR使用的
- 为什么不通过获取内联定义的静态常量整数成员变量的地址来违反 ODR?