Правильная инициализация статического массива constexpr в шаблоне класса?
Статические члены класса в C++ вызвали у меня небольшую путаницу из-за словесности стандарта:
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, похоже, связывает все соответствующим образом.
В качестве дополнительного вопроса, должны ли статические члены constexpr, не являющиеся массивами, определяться аналогичным образом (с определением шаблона вне класса)?
2 ответа
Я думаю, что вы хотите 9.4.2p3:
Если энергонезависимый
const static
элемент данных имеет целочисленный тип или тип перечисления, его объявление в определении класса может указывать инициализатор скобок или равных, в котором каждое предложение инициализатора, являющееся выражением присваивания, является константным выражением (5.19). Статический член данных литерального типа может быть объявлен в определении класса с помощью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;
Отличие от статического члена данных constexpr, не являющегося массивом (т. Е. Целочисленным или перечисляемым), состоит в том, что его использование в преобразовании lvalue-to-rvalue не является odr-use; вам нужно будет только определить его, если взять его адрес или сформировать постоянную ссылку на него.
На случай, если это кому-нибудь поможет, у меня с 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
Я не претендую на то, является ли это "правильным". Я оставлю это более квалифицированным.