В C ++ 11 и C ++ 14 зачем мне constexpr
в следующем фрагменте:
class Foo {
static constexpr double X = 0.75;
};
тогда как этот выдает ошибку компилятора:
class Foo {
static const double X = 0.75;
};
и (что более удивительно) это компилируется без ошибок?
class Foo {
static const double X;
};
const double Foo::X = 0.75;
В C ++ 03 нам было разрешено предоставлять только инициализатор класса для статических переменных-членов константного интеграла типов перечисления, в C ++ 11 мы могли инициализировать статический член литерального типа в классе с помощью constexpr. Это ограничение было сохранено в C ++ 11 для константных переменных, в основном для совместимости будет C ++ 03, мы можем видеть это из закрытый выпуск 1826: const с плавающей точкой в константных выражениях который говорит:
Константное целое число, инициализированное константой, может использоваться в константных выражениях, но константная переменная с плавающей точкой, инициализированная константой, не может. Это было сделано намеренно, чтобы быть совместимым с C ++ 03, поощряя последовательное использование constexpr. Однако некоторые люди считают это различие удивительным.
CWG закрыла этот запрос как не дефект (NAD), в основном говоря:
что программисты, желающие, чтобы значения с плавающей точкой участвовали в константных выражениях, должны использовать constexpr вместо const.
Для справки N1804
Наиболее близкий проект стандарта C ++ 03 общедоступный в разделе 9.4.2
[Class.static.data] говорит:
Если статический член данных имеет константный интеграл или константный тип перечисления, его объявление в определении класса может
задайте инициализатор константы, который должен быть выражением интегральной константы (5.19). В этом случае член может появиться
в интегральных константных выражениях. Член по-прежнему должен быть определен в области имен, если он используется в программе и
определение области пространства имен не должно содержать инициализатор.
и проект стандартного раздела C ++ 11 9.4.2
[Class.static.data] говорит:
Если энергонезависимый постоянный статический член данных имеет целочисленный тип или тип перечисления, его объявление в классе
в определении можно указать скобку-или-равный-инициализатор, в которой каждое предложение-инициализатор, являющееся выражением присваивания
является постоянным выражением (5.19). Статический член данных литерального типа может быть объявлен в
определение класса с помощью спецификатора constexpr; если это так, в его объявлении должна быть указана инициализация-скобка
в котором каждое предложение инициализатора, которое является выражением присваивания, является константным выражением. […]
это почти то же самое в проекте стандарта C ++ 14.
Статические константные «определения» в классе на самом деле являются объявлениями. Когда переменная определена, компилятор выделяет память для этой переменной, но здесь дело обстоит не так, то есть получение адреса этих объектов static-const-in-class плохо сформировано, NDR.
Эти вещи должны быть включены в код, но это не так легко сделать с типами с плавающей запятой, поэтому это недопустимо.
Определяя ваши статические константные переменные вне класса, вы сообщаете компилятору, что это реальное определение — реальный экземпляр с расположением в памяти.