Инициализатор константного выражения для статического члена класса типа 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 12

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++119.4.2 [класс.статический.data] говорит:

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

Это почти то же самое в проекте стандарта C++14.

Статические определения const в классе на самом деле являются объявлениями. Когда переменная определена, компилятор выделяет память для этой переменной, но здесь это не так, т. е. адрес этих статических-const-в-классе вещей плохо сформирован, NDR.

Предполагается, что эти вещи должны быть обработаны в коде, но это не так просто сделать с типами с плавающей запятой, поэтому это не допускается.

Определяя статические переменные const вне класса, вы сигнализируете компилятор, что это реальное определение-реальный экземпляр с расположением в памяти.