17

我惊讶地发现,当没有类外定义时,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 似乎只关心我是否对nestedODR 跳闸没有课外定义(例如,通过获取地址)。这个标准是强制性的还是运气?

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

从谷歌搜索和 SO 我发现了几个不同的答案,但很难区分哪些是最新的 C++14。在 C++98/03 中,我相信只能在类中初始化整数类型。我认为C++14 将其扩展为“文字”类型,其中包括 constexpr 可构造的东西。我不知道这是否与说我可以在没有课外定义的情况下逍遥法外是一样的。

4

1 回答 1

7

因此,在 E 是一个类的情况下,它们看起来都违反了 odr,如果我们查看odr-use上的 cppreferences 页面,它会说:

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

在这种情况下,当我们在这一行调用复制构造函数时,我们会获取一个引用:

show(x.nested);

值得一提的是,违反 odr 的行为不需要诊断

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

更新

dyp 向我指出缺陷报告 1741,该报告质疑绑定到复制 ctor 的参考参数是否是 odr 使用:

这个 odr-use T::s 是否要求它有一个定义,因为它绑定到 S 的复制构造函数的引用参数?

结果是对 [basic.def.odr] 第 3 段的以下更改:

变量 x 其名称显示为潜在求值表达式 ex 是 odr-used 除非x 满足出现在常量表达式 (5.20 [expr.const]) 中应用左值到右值转换 (4.1 [conv.lval ]) 到 x 产生一个不调用任何非平凡函数的常量表达式 (5.20 [expr.const]),如果 x 是一个对象,则 ex 是表达式 e 的潜在结果集合中的一个元素,其中任一左值到右值的转换 (4.1 [conv.lval]) 应用于 e,或者 e 是丢弃值表达式(第 5 条 [expr])。这是 odr-used...

那么问题就变成了这种变化涵盖了哪些情况。看起来这些例子是好的:

//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

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

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

我们可以使用std::is_trivially_copyable确认这一点,现场观看

由于我最初陈述的相同原因,枚举案例仍然可以。

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

于 2015-08-14T15:47:32.557 回答