Теперь разрешены переопределения статических элементов данных constexpr? (но не встроенный const)?

Ниже не удается скомпилировать как gcc, так и clang в С++ 14, но с помощью С++ 1z:

struct Cls {
  static constexpr int N = 0;
};
constexpr int Cls::N;
constexpr int Cls::N;

Ошибка С++ 14 предсказуема: redefinition of ‘constexpr const int Cls::N’

Что изменилось, чтобы сделать это законным? Я нашел:

n4659 10.1.5 [dcl.constexpr]

Функция или статический член данных, объявленный с помощью constexpr specifier неявно является встроенной функцией или переменной

Итак, я думал, что это может иметь отношение к встроенным переменным, но следующее не выполняется для С++ 1z под обоими компиляторами

struct Cls {
  static inline const int N = 0;
};
inline const int Cls::N; // note, only one definition here

Ответы

Ответ 1

До С++ 17 вам нужно было повторно объявить все переменные static вне класса ровно одной единицей перевода (обычно каждая единица перевода является файлом .cpp и наоборот), но это необязательно). Как вы указали, С++ 17 вводит переменные-члены класса inline, а переменные static constexpr автоматически квалифицируются. Вам не разрешено переопределять переменные inline вне класса, как вы видели во втором примере, но исключение было сделано для constexpr, потому что ранее вы были разрешены (и на самом деле необходимы), чтобы сделать это, но синтаксис осуждается.

В [class.static.data] p2 он позволяет использовать этот синтаксис для не-встроенных членов ("Объявление элемента нестационарных статических данных в его определении класса не является определением и может быть неполный тип, отличный от cv void. Определение для статического элемента данных, который не определен встроенным в определении класса должны появиться в области пространства имен, охватывающей определение класса участников. ")

В следующем параграфе стандарт допускает constexpr объявления за пределами класса и требует их для данных не constexpr (выделено мной):

Если нестационарный нестационарный элемент const статических данных имеет тип интеграла или перечисления, его объявление в определении класса может указывать логический или равный-инициализатор в который каждый оператор-инициализатор, являющийся выражением присваивания, является постоянное выражение (8.20). Член все еще должен быть определен в область пространства имен, если она используется odr (6.2) в программе и определение области пространства имен не должно содержать инициализатор. Встроенный статический элемент данных может быть определен в определении класса и может указывать логический или равный-инициализатор. Если член объявленный спецификатором constexpr, он может быть обновлен в область пространства имен без инициализатора (это использование устарело, см. D.1). Объявления других статических данных не должны указывать скобки или равно-инициализатор.

И heres примечание о ностальгии, D.1 Переобучение статических элементов данных constexpr [des.static_constexpr]:

Для совместимости с предыдущими международными стандартами С++ элемент статических данных constexpr может быть избыточно переоформлен за пределами класс без инициализатора. Это использование устарело. [Пример:

struct A {
  static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)

- конец примера]