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

时间:2019-03-15 12:04:26

标签: c++ language-lawyer c++17 constexpr

考虑

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.

live demo

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

2 个答案:

答案 0 :(得分:24)

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

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

  

未定义为删除的显式默认函数仅在将其隐式声明为constexpr的情况下才可以声明为constevalconstexpr。如果某个函数在其第一个声明中被明确默认为默认值,则如果隐式声明为constexpr,则该隐式被视为。{p>

何时将副本分配运算符隐式声明为constexpr[class.copy.assign]/10

  

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

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

文字类型来自[basic.types]/10

  

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

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

         
        
    • 它有一个琐碎的析构函数,
    •   
    • [...]
    •   
  •   

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

另外两个很好,拒绝A2是一个叮叮当当。


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

答案 1 :(得分: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归档的名为Defaulted destructor prevents using constexpr on defaulted copy/move-operator的错误,它表现出与OP中的代码相同的症状。

错误报告状态中的注释:

  

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

  

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

问题中的代码也是如此。

@YSC指出,此处的另一个相关引用是:[dcl.fct.def.default]/3,其中指出:

  

未定义为删除的显式默认函数仅在将其隐式声明为constexpr的情况下才可以声明为constevalconstexpr。如果某个函数在其第一个声明中被明确默认为默认值,则如果隐式声明为constexpr,则该隐式被视为。{p>