const статический элемент данных без инициализатора

#include <complex>

struct S
{
  static std::complex<double> constexpr c;
};

gcc генерирует ошибку, поскольку отсутствует инициализатор. Clang и MSVC не генерируют ошибку.

Насколько я знаю, статический элемент constexpr должен иметь инициализатор, даже если он имеет тип класса, который имеет конструктор, который можно вызывать без аргументов (как в этом случае). К сожалению, у меня нет последнего стандарта C++ для подтверждения моего предположения.

Поэтому правильный код должен инициализироваться конструктором, например:

struct S
{
  static std::complex<double> constexpr c {};
};

Может ли кто-нибудь доказать, какой компилятор прав, а что неправильно?

Ответ 1

GCC ошибочен.

GCC использует правила С++ 14 для переменных constexpr, для которых требуется инициализатор. Это изменяется на P0386 (полужирный текст - это новый текст):

В 9.2.3.2p3 измените:

Если элемент non -volatile n on- inline const - статический элемент данных является интегральным или перечисляемым типом, его объявление в определении класса может указывать инициализатор ab race-or- equal-, в котором каждый пункт initializer- То есть выражение assignment- является постоянным выражением (5.20). Статический член данных типа literal может быть объявлен в определении класса с помощью спецификатора constexpr; если это так, в его декларации указывается скобка-равность -initializer, в которой каждое предложение initializer-, которое является присваиванием -expression, является постоянным выражением. [Примечание. В обоих случаях член может отображаться в постоянных выражениях. - конечная нота] Член еще быть определен в области видимости пространства имен, если это odr- используется (3.2) в программе, и определение области видимости пространства имен не должно содержать инициализатор. Статический член данных п инлайн может быть определен в определении класса и может указывать ab race-or-equal -initializer. Если член объявлен с помощью спецификатора constexpr, он может быть переопределен в области пространства имен без инициализатора (это использование устарело, см. DX). Объявления других статических данных не должны указывать ab race-or-equal -initializer.

Ответ 2

В этом конкретном случае есть два ответа:

  • Для C++ 14 gcc прав (т.е. Constexpr статический член данных должен иметь инициализатор).
  • Для C++ 17 и выше gcc ошибается, поскольку он отказывается компилировать соответствующий код.

Бывший случай: В проекте N3797 (C++ 14), 9.4.2.3 (Элементы статических данных) [class.static.data] (основное внимание):

static член данных типа literal может быть объявлен в определении класса с constexpr спецификатора constexpr; если это так, в его декларации указывается логический или равный-инициализатор, в котором каждое предложение-инициализатор, являющееся выражением-присваиванием, является постоянным выражением.

См. Также: http://en.cppreference.com/w/cpp/language/static#Constant_static_members.

Я сказал "в этом конкретном случае", потому что std::complex имеет специализацию для double которая является LiteralType. Поэтому применяется правило выше. Для общих (то есть нелиберальных) типов см. Ответ на кодкайзеры.

Последний случай: для C++ 17 см. Xskxzr ответ.

Ответ 3

Из dcl.constexpr # 1:

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

constexpr static члены данных неявно inline.

Также из класса # static.data-3, акцент мой:

inline static элемент данных может быть определен в определении class и может указывать brace-or-equal-initializer.


Таким образом, GCC ошибочен. не требуется строгое brace-or-equal-initializer выравнивание.

Ссылка: N4659 С++ 17 Черновик