Jos*_*vin 17 c++ gcc clang one-definition-rule c++14
我很惊讶地发现,当没有类外定义时,GCC和Clang在按值传递静态constexpr成员时是否给我一个链接器错误是不一致的:
#include <iostream>
#include <type_traits>
#include <typeinfo>
template <typename X>
void show(X)
{
std::cout << typeid(X).name() << std::endl;
}
template <typename T>
struct Foo
{
//static constexpr struct E {} nested {}; // works in gcc and clang
//static constexpr struct E {T x;} nested {}; // doesn't work in gcc
//static constexpr enum E {} nested {}; // works in gcc and clang
//static constexpr enum E { FOO } nested {}; // works in gcc and clang
//static constexpr struct E { constexpr E() {} constexpr E(const E&) {} T x=T();} nested {}; // works in gcc and clang
static constexpr struct E { constexpr E() {} constexpr E(const E&) = default; T x=T(); } nested {}; // doesn't work in gcc
};
int main()
{
Foo<int> x;
show(x.nested);
}
Run Code Online (Sandbox Code Playgroud)
片段可以在这里播放.
我想使用第一行的语法,没有类外定义:
static constexpr struct E {} nested {}; // works in gcc and clang
Run Code Online (Sandbox Code Playgroud)
如果没有成员E,Clang和GCC似乎只关心我是否没有课外定义,nested如果我去ODR(例如通过获取地址).这个标准是强制性还是运气?
当有成员时,GCC(5.2)似乎还要我手动定义constexpr复制构造函数.这是一个错误吗?
从谷歌搜索和SO我发现了几个不同的答案,但很难分开哪些是最新的C++ 14.在C++ 98/03中,我相信只能在类中初始化整数类型.我认为 C++ 14将其扩展为'literal'类型,其中包括constexpr可构造的东西.我不知道这是不是说我可以逃脱没有一个外层定义.
因此,在E是类的情况下,它们看起来都像是odr违规,如果我们在odr-use上查看cppreferences页面,它会说:
非正式地,如果一个对象的地址被占用,或者一个引用被绑定到它上面,则该对象被使用,并且如果对它进行函数调用或者取得其地址,则该函数被使用.如果一个对象或函数使用了odr,它的定义必须存在于程序的某个地方; 违反这一点的是链接时错误.
在这种情况下,当我们在这一行上调用复制构造函数时,我们会引用一个引用:
show(x.nested);
Run Code Online (Sandbox Code Playgroud)
值得注意的是,odr违规不需要诊断.
在某些情况下,如果我们使用-fno- elide -constructors,它看起来在构造函数省略与gcc的效果中看到了什么,我们在E是类的所有情况下都会得到错误.在枚举的情况下,应用左值到右值的转换,因此没有使用odr.
更新
dyp向我指出了缺陷报告1741,该报告质疑是否与复制文件的参考参数绑定是否为使用:
这个odr使用T :: s,要求它有一个定义,因为它绑定到S的复制构造函数的引用参数?
结果是对[basic.def.odr]第3段的以下更改:
变量x的名称显示为潜在评估的表达式ex,除非
x满足应用左值到右值转换的常量表达式(5.20 [expr.const])中出现的要求,否则使用odr (4.1 [conv.lval] ])到x产生一个不调用任何非平凡函数的常量表达式(5.20 [expr.const]),如果x是一个对象,则ex是表达式e的潜在结果集合的元素,其中左值到左值的转换(4.1 [conv.lval])应用于e,或e是废弃值表达式(第5条[expr]).这是有用的......
那么问题就变成了这种变化涵盖的案例.看来这些例子还可以:
//static constexpr struct E {} nested {}; // works in gcc and clang
//static constexpr struct E {T x;} nested {}; // doesn't work in gcc
static constexpr struct E { constexpr E() {} constexpr E(const E&) = default; T x=T(); } nested {}; // doesn't work in gcc
Run Code Online (Sandbox Code Playgroud)
由于副本是微不足道的,而这一点并非无足轻重:
//static constexpr struct E { constexpr E() {} constexpr E(const E&) {} T x=T();} nested {}; // works in gcc and clang
Run Code Online (Sandbox Code Playgroud)
我们可以使用std :: is_trivially_copyable来确认这一点,现场直播.
因为我原来说的原因,enum案件仍然可以.
缺陷还报告实施方面的差异.