Инициализатор константного выражения для статического члена класса типа double
В 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;
2 ответа:
В C++03 нам было разрешено предоставлять только инициализатор в классе для статических переменных-членов const integral типов перечисления, в C++11 мы могли инициализировать статический член литерального типа в классе с помощью constexpr. Это ограничение было сохранено в C++11 для переменных const главным образом для совместимости с C++03 мы можем видеть это из закрытого выпуска 1826: const с плавающей запятой в постоянных выражениях , который говорит:
Целое число const, инициализированное константой, может быть используется в константных выражениях,но переменная с плавающей запятой const, инициализированная константой, не может. Это было сделано намеренно, чтобы быть совместимым с C++03, поощряя последовательное использование constexpr. Однако некоторые люди находят это различие удивительным.
CWG в итоге закрыла этот запрос как не дефектный (NAD ), в основном говоря:
Что программисты, желающие, чтобы значения с плавающей запятой участвовали в постоянных выражениях, должны использовать пользователем, а не константный.
Для справки
N1804
самый близкий проект стандарта К C++03, общедоступный в разделе9.4.2
[класс.статический.data] говорит:Если статический элемент данных имеет тип const integral или const enumeration, его объявление в определении класса может укажите константу-инициализатор, которая должна быть выражением интегральной константы (5.19). В этом случае член может появиться в интегральных постоянных выражениях. Член все еще должен быть определяется в области пространства имен, если он используется в программе и определение области пространства имен не должно содержать инициализатора.
И проект стандартного раздела C++11
9.4.2
[класс.статический.data] говорит:Если энергонезависимый статический элемент данных const имеет интегральный или перечислительный тип, то его объявление в классе определение может указывать инициализатор фигурной скобки или равенства, в котором каждое предложение инициализатора является выражением присваивания является постоянной величиной выражение (5.19). Статический элемент данных литерального типа может быть объявлен в определение класса с помощью constexpr спецификатор; если это так, то его заявление должно указать бандаж или равно инициализатор в котором каждое предложение инициализатора, являющееся выражением присваивания, является постоянным выражением. [...]
Это почти то же самое в проекте стандарта C++14.
Статические определения const в классе на самом деле являются объявлениями. Когда переменная определена, компилятор выделяет память для этой переменной, но здесь это не так, т. е. адрес этих статических-const-в-классе вещей плохо сформирован, NDR.
Предполагается, что эти вещи должны быть обработаны в коде, но это не так просто сделать с типами с плавающей запятой, поэтому это не допускается.
Определяя статические переменные const вне класса, вы сигнализируете компилятор, что это реальное определение-реальный экземпляр с расположением в памяти.