GCC 和 Clang 在 constexpr 构造函数上的不同行为
GCC and Clang different behaviors on constexpr constructor
对于此结构:
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'不能在常量表达式中使用。哪个编译器是正确的?
虽然从makeWrapper()
返回Wrapper
时的副本或移动可以省略,但它需要与 C++14 一起存在。现有的复制构造函数是非constexpr
的,它的存在抑制了隐式移动构造函数的创建。因此,我认为 clang 是对的:您需要使复制构造函数成为constexpr
。
请注意,对于 C++17,代码可能会变得正确:有人建议在某些情况下强制使用复制省略:P0135r0。然而,这一变化似乎还没有出现在工作文件中。不过,它可能会在本周登陆(感谢@NicolBolas指出它还没有)。我还没有在邮件中看到更新的论文。
Clang 是正确的。它在 g++ 中工作,因为它会自动省略复制构造函数 (RVO)。如果你通过-fno-elide-constructors
.G++也会抱怨。
C++14标准不清楚constexpr
对象中的Copy-Elision。
[类.副本/32] ...(此处部分转载)
当满足消除复制/移动操作的条件时......所选构造函数必须可访问,即使调用是 被忽略了。
直到我们知道accessible
的定义?我们可以假设 g++ 也是正确的吗?
dcl.constexpr/9
对象声明中使用的 constexpr 说明符声明 对象作为常量。此类对象应具有文字类型,并且应 初始 化。如果它由构造函数调用初始化,则该调用 应为常量表达式 ([expr.const])。否则,或者如果 constexpr 说明符用于引用声明中,每个 出现在其初始值设定项中的全表达式应为常量 表达。
迪特马尔·库尔(Dietmar Kuhl)的回答告诉我们未来会发生什么。
演示:
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
在这里观看直播
两个编译器都是正确的。
constexpr
函数和初始化器的规则表明,不能调用非constexpr
函数。
复制 elision 的规则表明,是否调用非constexpr
复制构造函数是未指定的。
唯一的结论可能是未指定函数和初始化器是否满足constexpr
的要求。如果他们这样做,那么编译器必须接受它。如果没有,则编译器必须诊断问题。
- 构造函数SFINAE和继承在clang中失败
- 继承的构造函数,在 clang++3.9 中编译,在 g++ 7 中失败
- Clang-Tidy:移动构造函数通过调用复制构造函数来初始化类成员
- 在 GCC 中工作的外行构造函数模板在 Clang 中失败
- Clang 无法在赋值运算符/复制构造函数中检测到未初始化的类成员
- clang格式:如何将构造函数的初始值设定项列表的每个元素保存在单独的行上
- 为什么 gcc 和 clang 各自为这个程序产生不同的输出?(转换运算符与构造函数)
- constexpr(但不是真正的)构造函数在GCC中编译,而不是在Clang中编译
- Clang 和 GCC 在使用大括号表示法和initializer_list时在构造函数选择上存在分歧
- 继承默认构造函数在 gcc 中失败并在 clang 中工作,哪个有错误?
- 在 Clang 中生成默认构造函数
- 构造函数上的SFINAE在VC2017中工作,但在clang / gcc中不起作用
- 已删除的构造函数 - MSVC 报告错误,Clang 不报告
- Travis CI:Clang在应推导移动构造函数时推论复制构造函数
- GCC 和 Clang 在 constexpr 构造函数上的不同行为
- Clang AST:获取构造函数的 CXXCtorInitializer 列表,这些构造函数的声明不是定义
- 为什么 Clang++ 不在另一个静态库中运行全局对象构造函数?
- 具有 GCC 接受"noexcept"构造函数的程序,被 clang 拒绝
- 当设置-fno-elide构造函数时,clang Xcode 4.4.1是否有错误
- 变量args SFINAE默认构造函数在clang中工作,但在Visual Studio 2015中失败