Declaring defaulted assignment operator as constexpr: which compiler is right?

cpp*_*ner 31 c++ language-lawyer constexpr c++17

Consider

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;
};
Run Code Online (Sandbox Code Playgroud)

GCC and MSVC accept all three structs. Clang rejects A1 and A2 (but accepts A3), with the following error message:

<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.
Run Code Online (Sandbox Code Playgroud)

(live demo)

Which compiler is correct, and why?

Bar*_*rry 24

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

[dcl.fct.def.default] / 3说:

可以声明未定义为delete的显式默认函数,constexpr或者consteval仅在将其隐式声明为的情况下才声明constexpr。如果一个函数在其第一个声明中被显式默认,则隐式认为该函数constexpr是否为隐式声明。

复制赋值运算符何时隐式声明constexpr[class.copy.assign] / 10

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

  • X是文字类型,并且
  • [...]

文字类型在[basic.types] / 10中

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

  • [...]
  • 可能具有cv资格的类类型,具有以下所有属性:

    • 它有一个琐碎的破坏者,
    • [...]

A1没有琐碎的析构函数,因此其隐式副本赋值运算符不是constexpr。因此,该副本分配运算符的格式不正确(gcc和msvc bug可以接受)。

另外两个很好,并且拒绝是一个叮叮当当的错误A2


请注意我引用的[dcl.fct.def.default]的最后一位。constexpr如果您明确指定了默认值,则实际上不必添加。那将是隐含的constexpr

  • 如果不是“constexpr”,添加“constexpr”**应该**会给您一个错误,这可能非常有用。 (2认同)

P.W*_*P.W 8

C ++ 17标准指出:

15.8.2复制/移动分配运算符[class.copy.assign]
...

10当默认使用但未定义为删除的类X的复制/移动赋值运算符时,它被隐式定义(6.2)(例如,当通过重载解析选择它以分配给其类类型的对象时) ),或者在其首次声明后明确将其默认设置。的隐式定义的复制/移动赋值运算符是constexpr如果
(10.1) - X是一个文本类型,和
(10.2) -选择复制/移动每个直接基类子对象赋值运算符是一个constexpr函数,和
(10.3) -对于每一个非X-类类型(或其数组)的静态数据成员,被选择用来复制/移动该成员的赋值运算符是一个constexpr函数。

复制分配操作员在两种情况下都满足上述要求。在第一种情况下,由于非平凡的析构函数,我们具有非文字类型。

因此,我相信Clang在第二种情况下拒绝代码是错误的。

有一个用Clang归档的bug,标题为:Default destructor阻止在默认的copy / move-operator上使用constexpr,该症状表现出与OP中的代码相同的症状。

错误报告中的注释状态:

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

如果在复制分配运算符之前声明析构函数,该问题也将消失。

问题中的代码也是如此。

正如@YSC所指出的,这里的另一个相关引用是:[dcl.fct.def.default] / 3,其中指出:

可以声明未定义为delete的显式默认函数,constexpr或者consteval仅在将其隐式声明为的情况下才声明constexpr。如果一个函数在其第一个声明中被显式默认,则隐式认为该函数constexpr是否为隐式声明。