c – 传递静态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);
}

Snippet可以使用here播放。

我想使用第一行的语法,没有类的定义:

static constexpr struct E {} nested {}; // works in gcc and clang

当E中没有成员时,Clang和GCC似乎只关心我如果我去ODR(例如通过访问地址),我没有嵌套的超类定义。这个标准是强制还是运气?

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

从谷歌搜索和SO我已经找到了几个不同的答案,但很难分开哪些是最新的C 14。在C 98/03我相信只有整数类型可以在类内初始化。我认为C14将这种扩展扩展为包括constexpr可构建的东西的“文字”类型。我不知道这是否相同,说我被允许离开,没有一个超级的定义。

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

Informally, an object is odr-used if its address is taken, or a reference is bound to it, and a function is odr-used if a function call to it is made or its address is taken. If an object or a function is odr-used, its definition must exist somewhere in the program; a violation of that is a link-time error.

在这种情况下,当我们在这行上调用copy构造函数时,我们参考一下:

show(x.nested);

值得注意的是odr-violations do not require a diagnostic

它看起来你在某些情况下看到在构造函数elision与gcc的影响,如果我们使用-fno-elide-constructors我们得到一个错误,所有的情况下,E是一个类。在枚举的情况下,应用了lvalue-to-rvalue转换,因此没有odr使用。

更新

dyp指出我到defect report 1741哪个问题是否绑定到副本ctor的参考参数是odr使用或不:

Does this odr-use T::s, requiring it to have a definition, because of binding it to the reference parameter of S’s copy constructor?

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

A variable x whose name appears as a potentially-evaluated expression ex is odr-used unless x satisfies the requirements for appearing in a constant expression (5.20 [expr.const]) applying the lvalue-to-rvalue conversion (4.1 [conv.lval]) to x yields a constant expression (5.20 [expr.const]) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1 [conv.lval]) is applied to e, or e is a discarded-value expression (Clause 5 [expr]). this is 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

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

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

我们可以使用std::is_trivially_copyablesee it live确认。

枚举案例还是可以的,原因与我原来说的相同。

缺陷也报告实施方式差异。

http://stackoverflow.com/questions/31843093/odd-behavior-passing-static-constexpr-members-without-definitions-by-value

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:c – 传递静态constexpr成员的奇怪行为,没有按值定义