对于这个结构:
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'不能用于常量表达式。"哪个编译器是对的?
答案 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 ++也是正确的吗?
对象声明中使用的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
答案 2 :(得分:3)
两个编译器都是对的。
constexpr
函数和初始化程序的规则表明不能调用非constexpr
函数。
复制省略规则说未指定非constexpr
复制构造函数是否被调用。
唯一的结论是,未指定函数和初始化程序是否满足constexpr
的要求。如果他们这样做,那么编译器必须接受它。如果他们不这样做,那么编译器必须诊断问题。