不按值定义传递静态constexpr成员的奇怪行为



我惊讶地发现,当没有类外定义时,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);
}

代码片段可以在这里播放。

我想使用第一行的语法,没有类外定义:
static constexpr struct E {} nested {}; // works in gcc and clang

E中没有成员时,Clang和GCC似乎只关心如果我触发ODR(例如通过获取地址),我没有nested的类外定义。这个标准是强制的还是运气?

当有成员时,GCC(5.2)似乎还希望我手动定义一个constexpr复制构造函数。这是bug吗?

从谷歌和所以我找到了几个不同的答案,但很难区分哪些是最新的c++ 14。在c++ 98/03中,我认为只有整数类型可以在类内部初始化。我认为 c++ 14将其扩展为'literal'类型,其中包括constexpr可构造的东西。我不知道这是否等同于说我可以不使用out- class定义

在E是一个类的情况下它们看起来都像odr违规,如果我们查看odr-use的cppreferences页面它说:

非正式地说,如果对象的地址被获取,或者引用被绑定到对象上,则使用odr;如果函数被调用或其地址被获取,则使用odr。如果对象或函数是odr使用的,其定义必须存在于程序的某个地方;违反这一规定的是链接时间错误。

,在本例中,当调用这一行的复制构造函数时,我们接受一个引用:

show(x.nested);

同样值得注意的是,奇数违反不需要诊断。

它看起来就像你在某些情况下看到的在gcc中构造函数省略的效果,如果我们使用-fno-elide-constructors,我们会得到一个错误,所有情况下,E是一个类。在枚举的情况下,应用左值到右值的转换,因此没有多余的使用。

更新

dyp向我指出缺陷报告1741,该报告质疑绑定到复制因子的引用参数是否是一种异常使用:

是否因为将T::s绑定到s的复制构造函数的引用形参上而使用T::s,要求它有定义?

,结果是对[basic.def.odr]第3段的修改如下:

变量x的名字出现在一个潜在求值表达式ex中,除非x满足出现在常量表达式(5.20 [expr.const])中的要求对x应用左值到右值转换(4.1 [convr . conval]),得到一个常量表达式(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

由于复制因子是微不足道的,而这个不是微不足道的:

//static constexpr struct E { constexpr E() {} constexpr E(const E&) {} T x=T();} nested {}; // works in gcc and clang

我们可以使用std::is_trivially_copyable来确认这一点。

枚举情况仍然可以,原因与我最初陈述的相同。

缺陷还报告了实现中的差异。

相关内容

  • 没有找到相关文章

最新更新