Правильная инициализация статического массива constexpr в шаблоне класса? - программирование
Подтвердить что ты не робот

Правильная инициализация статического массива constexpr в шаблоне класса?

Статические члены класса в С++ вызвали небольшую путаницу для меня из-за стандартной формулировки:

9.4.2 Элементы статических данных [class.static.data]

Объявление статического члена данных в его определении класса не является определением...

Однако constexpr требуется инициализировать (AFAIK, не удалось найти цитату из стандарта) в своем объявлении (например, в определении класса).

Из-за ограничений на constexpr я фактически забыл о требовании для статических членов, которые будут определены вне класса, пока я не попытаюсь обратиться к статическому массиву constexpr. Этот связанный вопрос дает правильный способ определения члена массива, но мне интересно, как повлиять на это определение в шаблоне класса.

Вот что я закончил:

template<typename T>
class MyClass
{
private:
  static constexpr std::size_t _lut[256] = { /* ... */ };
  T _data;

public:
  static constexpr std::size_t GetValue(std::size_t n) noexcept
  {
    return _lut[n & 255];
  }

  // ...
};

template<typename T>
constexpr std::size_t MyClass<T>::_lut[256];

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

В качестве последующего вопроса должны быть определены аналогичные элементы non-array static constexpr (с определением шаблона вне класса)?

4b9b3361

Ответ 1

Я думаю, вы хотите 9.4.2p3:

Если энергонезависимый элемент данных const static имеет тип интеграла или перечисления, его объявление в определении класса может указывать логический или равный-инициализатор, в котором каждое предложение-инициализатор, являющееся выражением присваивания, является постоянное выражение (5.19). Статический член данных типа literal может быть объявлен в определении класса с помощью спецификатора constexpr; если это так, в его декларации указывается логический или равный-инициализатор, в котором каждое предложение-инициализатор, являющееся выражением-присваиванием, является постоянным выражением. [...] Член все еще должен быть определен в области пространства имен, если он используется в odr (3.2) в программе, а определение области пространства имен не должно содержать инициализатор.

Определение статического элемента данных шаблона - это объявление шаблона (14p1). Пример, приведенный в 14.5.1.3p1:

template<class T> class X {
  static T s;
};
template<class T> T X<T>::s = 0;

Однако, как и выше, член constexpr static или const static, чье объявление в классе указывает, что инициализатор не должен иметь инициализатор в определении области пространства имен, поэтому синтаксис будет выглядеть следующим образом:

template<class T> class X {
  const static T s = 0;
};
template<class T> T X<T>::s;

Разница с элементом статических константных элементов non-array (то есть интегралом или перечислением) заключается в том, что его использование в преобразовании lvalue-to-r не является неприемлемым; вам нужно будет определить его, только если он примет свой адрес или сформирует ссылку на него const.

Ответ 2

В случае, если это помогает кому-то, следующее работало для меня с GCC 4.7, используя constexpr:

template<class T> class X {
  constexpr static int s = 0;
};
template<class T> constexpr int X<T>::s; // link error if this line is omitted

Я не утверждаю, является ли это "правильным". Я оставлю это более квалифицированным.