Подтвердить что ты не робот

Почему GCC считает, что определение статического элемента constexpr должно быть отмечено constexpr?

[C++14: 7.1.5/1]: Спецификатор constexpr должен применяться только к определению шаблона переменной или переменной, декларации функции или шаблона функции или объявления статического элемента данных литерала типа (3.9). Если какое-либо объявление функции, шаблона функции или шаблона переменной имеет спецификатор constexpr, то все его объявления должны содержать спецификатор constexpr. [..]

Обратите внимание, что во втором предложении не упоминается "статический член данных", как это делает первое предложение, поэтому в этом отрывке нет требования, что все объявления (и здесь я рассматриваю специфическое определение) t21 > static имеет спецификатор constexpr.

Я не могу найти какое-либо правило в другом месте для этого.

Почему же GCC отклоняет следующую программу?

#include <chrono>

using namespace std::chrono_literals;

#define DUR 1000ms

struct T
{
   static constexpr auto dur_1 = DUR;
};

decltype(T::dur_1) T::dur_1;

// main.cpp:12:23: error: 'constexpr' needed for in-class initialization of static data member 'const std::chrono::duration<long int, std::ratio<1l, 1000l> T::dur_1' of non-integral type [-fpermissive] 
// decltype(T::dur_1) T::dur_1;
//                       ^
4b9b3361

Ответ 1

Это выглядит неуточненным для меня, я не вижу явного требования, но мы можем понять, почему это проблема из отчет о дефекте 699: Должны Constexpr члены функции быть определены в спецификации члена класса?, который, хотя и имеет дело с функциями constexpr, говорит следующее (основное внимание):

Если запрет был смягчен, чтобы разрешить раздельное объявление и определение функций члена constexpr, некоторые вопросы отвечать, например, должен ли отображаться спецификатор constexpr как объявление, так и определение (спецификатор inline не нужен). Если оно могут быть опущены в одном или другом случае, есть проблема удобства использования относительно того, что constexpr подразумевает const; определитель констант необходимо будет явно указывать в декларации, в которой constexpr был опущен.

Хотя в этом случае добавление const не решает проблему, хотя в более простых случаях это, похоже, решает проблему. Мы можем видеть в более простом случае, как clang, так и gcc требуют либо const, либо constexpr:

struct T
{
   static constexpr int blah = 1 ;
};

const int T::blah ;

Обновить

Этот отчет об ошибке gcc: Bogus "error: redeclaration... отличается от" constexpr" имеет следующую цитату от Ричарда Смита:

Нет правила, требующего последовательных объявлений переменных для соглашаются в "constexpr'ness" (это правило применимо только к функциям).

Итак, это похоже на ошибку gcc, хотя по-прежнему кажется, что она может использовать некоторую ясность в стандарте.

Ответ 2

Элемент constexpr static data должен быть инициализирован в классе согласно 7.1.5 (9). Это определение члена. Из-за ODR никакое другое определение не допускается, поэтому T:: dur_1 может быть только объявлением. Но нет правила, разрешающего объявление константных статических элементов данных вне тела класса, поэтому это объявление недопустимо.

GCC поддерживает это как расширение только в том случае, если constexpr используется последовательно.

Или я ошибаюсь, и это ошибка;)

FWIW: clang принимает этот код без предупреждения.

Ответ 3

В моем предыдущем ответе было написано, что проблема с кодом заключалась в том, что объекты std:: chrono_literals недействительны как constexp s, но, как указывает Lightspeed, это не так.

Я сделал немного больше исследований и определил, что проблема с вашим кодом - это ваш struct t: в частности, эта строка:

static constexp auto ...

Есть еще один ответ на SO об этом здесь

Чтобы четко указать, почему это ваша проблема (поскольку комментарий указывает, что это не сразу очевидно):

Любое выражение, помеченное constexp, должно быть определено во время компиляции. Это ты уже знаешь. Когда вы пытаетесь создать экземпляр с выражением decltype(T::dur_1) T:dur_1, в ваших глазах вы предоставляете правильные учетные данные конструктору хронологического литерала (который равен constexp). Проблема в том, что тип явно не определен, поскольку вы, кажется, думаете, что это из вашего предварительного процессора определения замены DUR 1000 мс.

Попробуйте следующее:

template <class T>
struct foo {
    static constexpr auto dur_1 = DUR;
    typedef decltype(DUR) milliseconds;
}

template <class T>
constexp milliseconds foo<T>::milliseconds foo<T>::DUR; 

Удалив неспособность компилятора GCC для определения автоматического типа во время компиляции через явное определение, вы должны решить свою проблему.

Вот почему была дана исходная ссылка. GCC неправильно не может определить автоматическую типизацию во время компиляции.