Инициализатор константного выражения для статического члена класса типа double

В С++ 11 и С++ 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;

Ответ 1

В С++ 03 нам было разрешено предоставлять инициализатор in-класса для статических переменных-членов const-интеграла типов перечисления, в С++ 11 мы могли бы инициализировать статический член типа literal в классе с помощью constexpr. Это ограничение было сохранено в С++ 11 для константных переменных, главным образом для совместимости, будет С++ 03, мы можем видеть это из закрытая проблема 1826: const с плавающей запятой в константе выражения, в котором говорится:

Сопряженное целое число, инициализированное константой, может использоваться в константных выражениях, но переменная с плавающей запятой const, инициализированная константой, не может. Это было преднамеренно, чтобы быть совместимым с С++ 03, поощряя последовательное использование constexpr. Однако некоторые люди считают это различие удивительным.

CWG закрыл этот запрос как не дефект (NAD), в основном говоря:

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

Для справки N1804 самый близкий проект стандарта до С++ 03, общедоступный в разделе 9.4.2 [class.static.data], говорит:

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

и в стандартном разделе проекта С++ 11 9.4.2 [class.static.data] говорится:

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

это почти то же самое в черновом стандарте С++ 14.

Ответ 2

В-статических константных "определениях" на самом деле есть декларации. Когда переменная определена, компилятор выделяет память для этой переменной, но здесь это не так, т.е. С учетом того, что адрес этих статических конструкций в классе не сформирован, NDR.

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

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