将默认赋值运算符声明为constexpr:哪个编译器是对的?

问题描述 投票:31回答:2

考虑

struct A1 {
    constexpr A1& operator=(const A1&) = default;
    ~A1() {}
};
struct A2 {
    constexpr A2& operator=(const A2&) = default;
    ~A2() = default;
};
struct A3 {
    ~A3() = default;
    constexpr A3& operator=(const A3&) = default;
};

GCC和MSVC接受所有三种结构。 Clang拒绝A1A2(但接受A3),并出现以下错误消息:

<source>:2:5: error: defaulted definition of copy assignment operator is not constexpr
    constexpr A1& operator=(const A1&) = default;
    ^
<source>:6:5: error: defaulted definition of copy assignment operator is not constexpr
    constexpr A2& operator=(const A2&) = default;
    ^
2 errors generated.

(Qazxswpoi)

哪个编译器是正确的,为什么?

c++ language-lawyer c++17 constexpr
2个回答
24
投票

我认为所有三个编译器都是错误的。

live demo说:

未定义为已删除的显式默认函数只有在被隐式声明为[dcl.fct.def.default]/3时才可以声明为constexprconsteval。如果函数在其第一个声明中显式默认,则隐式声明将隐式地认为是constexpr

何时复制赋值运算符隐式声明constexprconstexpr

隐式定义的复制/移动赋值运算符是constexpr if

  • X是文字类型,和
  • [...]

文字类型的位置,来自[class.copy.assign]/10

类型是文字类型,如果它是:

  • [...]
  • 一个可能具有cv限定的类类型,它具有以下所有属性: 它有一个简单的析构函数, [...]

[basic.types]/10没有一个简单的析构函数,因此它的隐式复制赋值运算符不是A1。因此,复制赋值运算符格式不正确(gcc和msvc错误接受)。

另外两个很好,这是一个拒绝constexpr的铿锵声。


注意我引用的[dcl.fct.def.default]的最后一位。如果您明确默认,则实际上不必添加A2。这可能是隐含的constexpr


8
投票

C ++ 17标准规定:

15.8.2复制/移动赋值运算符constexpr ...

[class.copy.assign]默认使用默认情况下默认定义为已删除的类X的复制/移动赋值运算符(6.2)(例如,当通过重载决策选择它以分配给其类类型的对象时) )或在第一次声明后明确违约时。隐式定义的复制/移动赋值运算符是10 if (10.1) - constexpr是一个字面类型,和 (10.2) - 选择复制/移动每个直接基类子对象的赋值运算符是X函数,并且 (10.3) - 对于类型(或其数组)的constexpr的每个非静态数据成员,选择复制/移动该成员的赋值运算符是X函数。

复制赋值运算符在两种情况下满足上述要求。在第一种情况下,由于非平凡的析构函数,我们有一个非文字类型。

所以我认为Clang在第二种情况下拒绝代码是错误的。

有一个提交给Clang的错误标题:constexpr,它显示与OP中的代码相同的症状。

错误报告的评论说明:

当默认的析构函数被注释掉(即没有用户声明)时,错误就不复存在了。

如果在复制赋值运算符之前声明析构函数,问题也会消失。

对于问题中的代码也是如此。

正如@YSC指出的那样,另一个相关的引用是:Defaulted destructor prevents using constexpr on defaulted copy/move-operator,其中指出:

未定义为已删除的显式默认函数只有在被隐式声明为[dcl.fct.def.default]/3时才可以声明为constexprconsteval。如果函数在其第一个声明中显式默认,则隐式声明将隐式地认为是constexpr

© www.soinside.com 2019 - 2024. All rights reserved.