提问者:小点点

GCC和Clang在constexpr构造函数上的不同行为


对于此结构:

struct Wrapper {
    int value;

    constexpr explicit Wrapper(int v) noexcept : value(v) {}
    Wrapper(const Wrapper& that) noexcept : value(that.value) {}
};

这个功能是:

constexpr Wrapper makeWrapper(int v)
{
    return Wrapper(v);
}

以下代码无法为Clang(Apple LLVM版本7.3.0)编译,但对GCC(4.9)编译良好,两者都具有-Wall-Wpert-Werw-peandan-error

constexpr auto x = makeWrapper(123);

Clang抱怨“非constexpr构造函数‘Wrapper’不能在常量表达式中使用。”哪个编译器是对的?


共3个答案

匿名用户

虽然可以省略从makeWrapper()返回Wrapper时的复制或移动,但它需要在C 14中存在。现有的拷贝构造函数是非Constexpr,它的存在抑制了隐式移动构造函数的创建。因此,我认为clang是正确的:您需要将复制构造函数设置为Constexpr

请注意,使用C 17,代码可能会变得正确:在某些情况下,建议强制执行复制省略:P0135r0。然而,这一变化似乎还没有出现在工作文件中。不过,它可能会在本周着陆(感谢@NicolBolas指出它还没有着陆)。我在邮件中没有看到更新的论文。

匿名用户

叮当是正确的。它在g中工作,因为它自动省略了复制构造函数(RVO)。如果你传递-fno-elide-构造函数。g也会抱怨。

C14标准不清楚Constrexr对象中的Copy-Elision。

[class.copy/32]。。。(此处部分转载)

当满足复制/移动操作的省略标准时......即使省略了调用,选定的构造函数也必须是可访问的。

直到我们知道可访问性的定义?我们可以假设g也是正确的?

dcl。constexpr/9

对象声明中使用的constexpr说明符将对象声明为const。此类对象应具有文字类型,并应初始化。如果由构造函数调用初始化,则该调用应为常量表达式([expr.const])。否则,或者如果引用声明中使用了constexpr说明符,则其初始值设定项中出现的每个完整表达式都应为常量表达式。

迪特马尔·库尔的回答告诉我们未来会发生什么。

演示:

struct Wrapper {
    int value;

    constexpr explicit Wrapper(int v) noexcept : value(v) {}
    Wrapper(const Wrapper& that) noexcept : value(that.value)  {}
};

constexpr Wrapper makeWrapper(int v)
{
    return Wrapper(v);
}

int main()
{
    constexpr auto x = makeWrapper(123);
}

编译

g++ -std=c++14 -Wall -pedantic -fno-elide-constructors main.cpp && ./a.out

看它住在这里

匿名用户

两个编译器都是对的。

函数和初始化函数的规则规定不能调用非函数。

复制省略规则表示,未指定是否调用非constexprcopy构造函数。

唯一的结论是函数和初始化器是否满足constexpr的要求尚不明确。如果他们这样做,那么编译器必须接受它。如果没有,则编译器必须诊断问题。