Рассмотреть возможность
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.
Какой компилятор правильный и почему?





Стандарт С++ 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
constexprif
(10.1) —Xis a literal type, and
(10.2) — the assignment operator selected to copy/move each direct base class subobject is aconstexprfunction, and
(10.3) — for each non-static data member ofXthat is of class type (or array thereof), the assignment operator selected to copy/move that member is aconstexprfunction.
Оператор копирования-присваивания удовлетворяет вышеуказанным требованиям в двух случаях. В первом случае у нас нелитеральный тип из-за нетривиального деструктора.
Поэтому я считаю, что Clang ошибается, отказываясь от кода во втором случае.
В Clang зарегистрирована ошибка под названием: Деструктор по умолчанию предотвращает использование constexpr для оператора копирования/перемещения по умолчанию, которая показывает те же симптомы, что и код в 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
constexprorconstevalonly if it would have been implicitly declared asconstexpr. If a function is explicitly defaulted on its first declaration, it is implicitly considered to beconstexprif the implicit declaration would be.
связанные: eel.is/c++draft/dcl.fct.def.default#3 и угорь.is/c++draft/class.copy.assign#10 (уже в кавычках)
Могу ли я перевести это как: изменение порядка определения деструктора и копирования (как это сделал OP для struct A2 и struct A3) не должно иметь никакого эффекта (для компиляции кода без ошибок)?
Я думаю, что все три компилятора ошибаются.
[dcl.fct.def.default]/3 говорит:
An explicitly-defaulted function that is not defined as deleted may be declared
constexprorconstevalonly if it would have been implicitly declared asconstexpr. If a function is explicitly defaulted on its first declaration, it is implicitly considered to beconstexprif the implicit declaration would be.
Когда неявно объявляется оператор присваивания копии constexpr? [класс.копия.назначить]/10:
The implicitly-defined copy/move assignment operator is constexpr if
- X is a literal type, and
- [...]
Где литеральный тип, из [основные.типы]/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 там, где это возможно.
Добавление constexprдолжен даст вам ошибку, если это не constexpr, что может быть весьма полезно.
Хороший вопрос. Дальше только предположение... Мы знаем, что
constexprв функциях не означаетconst. Это означает, может ли эта функция быть вычислена во время компиляции. Оператору присваивания копирования, созданному без вывода сообщений, не предшествуетconstexpr. Это означает, чтоconstexprу вас есть перегрузка по сравнению с молча созданным. Однако перегрузка не может быть установлена по умолчанию, что объясняет ошибку. Ознакомьтесь со следующими тремя примерами кода: 1) (clang) rextester.com/WLGFD87794, 2) (gcc) rextester.com/RMWQ86797, 3) (vc++) rextester.com/MXIHQ50551 .