将默认赋值运算符声明为 constexpr:哪个编译器是正确的?
Declaring defaulted assignment operator as constexpr: which compiler is right?
考虑
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 拒绝 A1
和 A2
(但接受 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.
哪个编译器是正确的,为什么?
C++17 标准规定:
15.8.2 Copy/move assignment operator [class.copy.assign]
...
10 A copy/move assignment operator for a class X that is defaulted and not defined as deleted is implicitly defined when it is odr-used (6.2) (e.g., when it is selected by overload resolution to assign to an object of its class type) or when it is explicitly defaulted after its first declaration. The implicitly-defined copy/move assignment operator is constexpr
if
(10.1) — X
is a literal type, and
(10.2) — the assignment operator selected to copy/move each direct base class subobject is a constexpr
function, and
(10.3) — for each non-static data member of X
that is of class type (or array thereof), the assignment operator selected to copy/move that member is a constexpr
function.
copy-assignment运算符满足上述两种情况的要求。在第一种情况下,由于 non-trivial 析构函数,我们有一个 non-literal 类型。
所以我认为 Clang 在第二种情况下拒绝代码是错误的。
Clang 提交了一个错误,标题为:Defaulted destructor prevents using constexpr on defaulted copy/move-operator,它显示了与 OP 中的代码相同的症状。
错误报告中的评论状态:
When defaulted destructor is commented out (i.e. not user declared), then errors cease to exist.
和
The problem also goes away if you declare the destructor before the copy assignment operator.
问题中的代码也是如此。
正如@YSC 指出的那样,这里的另一个相关引用是:[dcl.fct.def.default]/3 其中指出:
An explicitly-defaulted function that is not defined as deleted may be declared constexpr
or consteval
only if it would have been implicitly declared as constexpr
. If a function is explicitly defaulted on its first declaration, it is implicitly considered to be constexpr
if the implicit declaration would be.
我认为三个编译器都错了。
An explicitly-defaulted function that is not defined as deleted may be declared constexpr
or consteval
only if it would have been implicitly declared as constexpr
. If a function is explicitly defaulted on its first declaration, it is implicitly considered to be constexpr
if the implicit declaration would be.
什么时候隐式声明复制赋值运算符constexpr
? [class.copy.assign]/10:
The implicitly-defined copy/move assignment operator is constexpr if
- X is a literal type, and
- [...]
字面量类型来自[basic.types]/10:
A type is a literal type if it is:
- [...]
a possibly cv-qualified class type that has all of the following properties:
- it has a trivial destructor,
- [...]
A1
没有平凡的析构函数,因此它的隐式复制赋值运算符不是 constexpr
。因此,该复制赋值运算符格式错误(接受 gcc 和 msvc 错误)。
另外两个没问题,拒绝A2
.
是个铿锵bug
注意我引用的 [dcl.fct.def.default] 的最后一点。如果您明确默认,则实际上不必添加 constexpr
。在可能的情况下,它会隐式地 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 拒绝 A1
和 A2
(但接受 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.
哪个编译器是正确的,为什么?
C++17 标准规定:
15.8.2 Copy/move assignment operator [class.copy.assign]
...10 A copy/move assignment operator for a class X that is defaulted and not defined as deleted is implicitly defined when it is odr-used (6.2) (e.g., when it is selected by overload resolution to assign to an object of its class type) or when it is explicitly defaulted after its first declaration. The implicitly-defined copy/move assignment operator is
constexpr
if
(10.1) —X
is a literal type, and
(10.2) — the assignment operator selected to copy/move each direct base class subobject is aconstexpr
function, and
(10.3) — for each non-static data member ofX
that is of class type (or array thereof), the assignment operator selected to copy/move that member is aconstexpr
function.
copy-assignment运算符满足上述两种情况的要求。在第一种情况下,由于 non-trivial 析构函数,我们有一个 non-literal 类型。
所以我认为 Clang 在第二种情况下拒绝代码是错误的。
Clang 提交了一个错误,标题为:Defaulted destructor prevents using constexpr on defaulted copy/move-operator,它显示了与 OP 中的代码相同的症状。
错误报告中的评论状态:
When defaulted destructor is commented out (i.e. not user declared), then errors cease to exist.
和
The problem also goes away if you declare the destructor before the copy assignment operator.
问题中的代码也是如此。
正如@YSC 指出的那样,这里的另一个相关引用是:[dcl.fct.def.default]/3 其中指出:
An explicitly-defaulted function that is not defined as deleted may be declared
constexpr
orconsteval
only if it would have been implicitly declared asconstexpr
. If a function is explicitly defaulted on its first declaration, it is implicitly considered to beconstexpr
if the implicit declaration would be.
我认为三个编译器都错了。
An explicitly-defaulted function that is not defined as deleted may be declared
constexpr
orconsteval
only if it would have been implicitly declared asconstexpr
. If a function is explicitly defaulted on its first declaration, it is implicitly considered to beconstexpr
if the implicit declaration would be.
什么时候隐式声明复制赋值运算符constexpr
? [class.copy.assign]/10:
The implicitly-defined copy/move assignment operator is constexpr if
- X is a literal type, and
- [...]
字面量类型来自[basic.types]/10:
A type is a literal type if it is:
- [...]
a possibly cv-qualified class type that has all of the following properties:
- it has a trivial destructor,
- [...]
A1
没有平凡的析构函数,因此它的隐式复制赋值运算符不是 constexpr
。因此,该复制赋值运算符格式错误(接受 gcc 和 msvc 错误)。
另外两个没问题,拒绝A2
.
注意我引用的 [dcl.fct.def.default] 的最后一点。如果您明确默认,则实际上不必添加 constexpr
。在可能的情况下,它会隐式地 constexpr
。