C ++ 17 §10.1.5 / 1 состояния:
Спецификатор constexpr
должен применяться только к определению переменной или шаблона переменной илиобъявление функции или шаблона функции.Функция или член статических данных, объявленные с помощью спецификатора constexpr
, неявно являются встроенной функцией или переменной (10.1.6).Если какое-либо объявление функции или шаблона функции имеет спецификатор constexpr
, то все его объявления должны содержать спецификатор constexpr
.
Аналогичный абзац существует в стандарте начиная с C ++ 11(§7.1.5 / 1), который цитируется в комментарии Ричарда Смита , в котором он утверждает, что стандарт C ++ не требует, чтобы спецификатор constexpr
совпадал между объявление и определение переменной.Последнее утверждение вышеприведенного абзаца явно требует, чтобы спецификатор constexpr
совпадал между объявлениями функции и шаблона функции , но не упоминает объявления переменных.
§10.1.5 / 9 состояния:
Спецификатор constexpr
, используемый в объявлении объекта, объявляет объект как const
.Такой объект должен иметь буквальный тип и должен быть инициализирован.В любом объявлении переменной constexpr
полное выражение инициализации должно быть константным выражением (8.20).
Конечно, если у нас есть отдельное объявление и определение, они оба должны будут соответствоватьв const
ness, независимо от того, требуется ли совпадение спецификаторов constexpr
.
§12.2.3.2 / 2-3 говорит:
2 Объявление не встроенного статического члена данных в его определении класса не является определением и может иметь неполный тип, отличный от cv void
.Определение статического члена данных, который не определен встроенным в определении класса, должен появиться в области пространства имен, включающей определение класса члена.В определении в области пространства имен имя члена статических данных должно быть квалифицировано по имени его класса с помощью оператора ::.Выражение initializer в определении элемента статических данных входит в область его класса (6.3.7).
3 Если энергонезависимые не встроенные const
статические данныеэлемент имеет целочисленный тип или тип перечисления ... Если элемент объявлен с помощью спецификатора constexpr
, он может быть повторно объявлен в области пространства имен без инициализатора (такое использование не рекомендуется; см. D.1).Объявления других элементов статических данных не должны указывать скобка-или-равно-инициализатор .
§D.1 / 1 читает:
Для совместимости с предыдущими международными стандартами C ++ элемент constexpr
статических данных может быть избыточно объявлен вне класса без инициализатора.Это использование устарело.
Из которого мы можем сделать вывод, что если элемент объявлен со спецификатором constexpr
, то область имен definition равнаизбыточность и выражение инициализатора должны быть в паре с объявлением и должны быть опущены в определении / переопределении .
В качестве полного примера я предлагаю случай статического члена его собственного класса литерального типа (который не может быть инициализирован в классе):
struct S
{
static S const ZERO; // not marked `constexpr`, but still `const`
constexpr S(int value = {}) : _value{ value } {}
int const _value;
};
constexpr S S::ZERO{ 0 }; // implicitly `inline` (if C++17) and `const`
Эта интерпретацияconstexpr
Использование со статическими членами данных поддерживается GCC, Clang и MSVC, хотя Мне сказали, что это неправильно .
Является ли нарушение несоответствиемиспользование спецификатора constexpr
в объявлениях и определениях переменных?
IЕсли это на самом деле нарушение, то невозможно правильно определить constexpr
статический член данных своего собственного класса, так как определения в классе запрещены, потому что тип является неполным, а определения вне класса запрещены для включенияинициализатор, если объявление в классе помечено спецификатором constexpr
.