Объявление оператора присваивания по умолчанию как constexpr: какой компилятор прав? - PullRequest
31 голосов
/ 15 марта 2019

Рассмотрим

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.

( live demo )

Какой компилятор правильный и почему?

Ответы [ 2 ]

24 голосов
/ 15 марта 2019

Я думаю, что все три компилятора неверны.

[dcl.fct.def.default] / 3 говорит:

Явно дефолтная функциято, что не определено как удаленное, может быть объявлено constexpr или consteval только в том случае, если оно неявно было объявлено как constexpr.Если для функции явно задано значение по умолчанию в ее первом объявлении, оно неявно считается constexpr, если неявное объявление будет.

Когда оператор присваивания копии неявно объявляется constexpr? [class.copy.assign] / 10 :

Неявно определенный оператор назначения копирования / перемещения - constexpr, если

  • X - литералtype и
  • [...]

Где буквенный тип, от [basic.types] / 10 :

Тип является литеральным типом, если он является:

  • [...]
  • возможно, квалифицированным cv типом класса, который имеет всеследующие свойства:

    • имеет тривиальный деструктор,
    • [...]

A1 не имеет тривиального деструктора, поэтому его оператор неявного копирования не равен constexpr.Следовательно, оператор присваивания копии некорректен (ошибка gcc и msvc для принятия).

Два других в порядке, и отклонение ошибки A2.

Обратите внимание на последний бит [dcl.fct.def.default], который я цитировал.Вам на самом деле не нужно добавлять constexpr, если вы явно по умолчанию.Было бы неявно constexpr, где это возможно.

8 голосов
/ 15 марта 2019

Стандарт C ++ 17 гласит:

15.8.2 Оператор назначения копирования / перемещения [class.copy.assign]
...

10 Оператор назначения копирования / перемещения для класса X, который по умолчанию и не определен как удаленный, неявно определяется, когда используется odr (6.2) (например, когдаон выбирается с помощью разрешения перегрузки для назначения объекту своего типа (или когда он явно установлен по умолчанию после его первого объявления). Неявно определенный оператор присваивания копирования / перемещения имеет вид constexpr, если
(10.1) - X является литеральным типом, а
(10.2) - оператор присваивания, выбранный для копирования /Переместить каждый подобъект прямого базового класса - это функция constexpr, а
(10.3) - для каждого не статического члена данных X, который имеет тип класса (или его массив), оператор присваивания, выбранный для копирования /переместить этот элемент является функцией constexpr.

Оператор копирования-назначения удовлетворяет вышеуказанным требованиям в двух случаях.В первом случае у нас есть не-литеральный тип из-за нетривиального деструктора.

Поэтому я считаю, что Clang неправильно отклоняет код во втором случае.

Существуетошибка, поданная в Clang под названием: Деструктор по умолчанию не позволяет использовать constexpr для оператора копирования / перемещения , который показывает те же симптомы, что и код в OP.

Комментарии из состояния отчета об ошибке:

Когда по умолчанию деструктор закомментирован (т.е. не объявлен пользователем), ошибки перестают существовать.

и

Проблема такжепропадает, если вы объявите деструктор перед оператором присваивания копии.

Это верно и для кода в вопросе.

Как указывает @YSC, здесь есть еще одна важная цитатаis: [dcl.fct.def.default] / 3 , в котором говорится:

Явно заданная по умолчанию функция, которая не определена как удаленная, может быть объявлена ​​constexpr или consteval только если бы это было неявно деОбозначается как constexpr.Если функция явно установлена ​​по умолчанию в своем первом объявлении, она неявно считается равной constexpr, если неявное объявление будет.

...