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

时间:2016-06-20 21:25:50

标签: c++ c++11

对于这个结构:

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 -Wextra -Werror -pedantic-errors

constexpr auto x = makeWrapper(123);

Clang抱怨"非constexpr构造函数' Wrapper'不能用于常量表达式。"哪个编译器是对的?

3 个答案:

答案 0 :(得分:14)

虽然可以省略从Wrapper返回makeWrapper()时的复制或移动,但它必须与C ++ 14一起存在。现有的复制构造函数是非constexpr,它的存在禁止创建隐式移动构造函数。因此,我认为clang是对的:您需要将复制构造函数设为constexpr

请注意,使用C ++ 17时,代码可能会变得正确:有一项建议要求在某些情况下强制执行copy-elision:P0135r0。然而,似乎这种变化还没有落在工作文件中。它可能会在本周登陆(感谢@NicolBolas指出它还没有)。我还没有看到mailing中的最新论文。

答案 1 :(得分:4)

Clang是对的。它在g ++中工作,因为它会自动删除复制构造函数(RVO)。如果你通过-fno-elide-constructors。 g ++也会抱怨。

C ++ 14标准不清楚constexpr对象中的Copy-Elision ..

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

  

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

在我们知道accessible的定义之前?我们可以假设g ++也是正确的吗?

dcl.constexpr/9

  

对象声明中使用的constexpr说明符声明了   对象为const。这样的物体应具有字面类型,并且应为   初始化。如果它是由构造函数调用初始化的那个调用   应该是一个常量表达式([expr.const])。否则,或者如果是   constexpr说明符用于引用声明中   出现在其初始化程序中的完整表达式应为常量   表达

Dietmar Kuhl's answer告诉我们未来的发展方向。

演示:

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

live here

答案 2 :(得分:3)

两个编译器都是对的。

constexpr函数和初始化程序的规则表明不能调用非constexpr函数。

复制省略规则说未指定非constexpr复制构造函数是否被调用。

唯一的结论是,未指定函数和初始化程序是否满足constexpr的要求。如果他们这样做,那么编译器必须接受它。如果他们不这样做,那么编译器必须诊断问题。