c++ c++11 static-members constexpr

c++ - ¿Por qué los miembros de datos estáticos no integrales inicializados en la clase deben ser constexpr?



c++11 static-members (3)

Los miembros de datos integrales estáticos inicializados en la definición de clase pueden declararse const o constexpr , pero los miembros de datos estáticos no integrales inicializados en la definición de clase deben ser constexpr :

class MyClass { static const int w = 5; // okay static constexpr int x = 5; // okay static const float y = 1.5; // error! static constexpr float z = 1.5; // okay };

¿Alguien sabe por qué la declaración para y no está permitida? La parte del Estándar que lo hace ilegal es 9.4.2 / 3, pero ¿por qué es ilegal?


Antes de C ++ 11, no podía inicializar miembros estáticos de tipos no integrales / de enumeración en la declaración de clase (pero puede hacerlo fuera de la declaración de clase). La regla que rige constexpr lleva hacia adelante, pero le permite inicializarlo usando constexpr en la declaración de clase (por lo que ya no necesita código como el siguiente):

struct A { static const float pi; }; const float A::pi = 3.1415;

Uno de los efectos secundarios de esta regla fue simplificar la estructura de su clase en lugar de hacerlo feo (como el código anterior).

Una de las razones por las que este fue el caso antes de la adición de constexpr en C ++ 11 fue que el estándar no especificaba cómo se debían implementar los puntos flotantes (se deja al procesador / arquitectura, por ejemplo, cuando se dice float x = 1.6f , en realidad es 1.6000000000024 en la mayoría de los sistemas).


Puede deberse al hecho de que la no integral i también puede incluir el tipo de datos como char y es por eso que no puede hacer que sean constantes y requiere una expresión constante. Pero en el caso de la integral, puede convertirlos en expresiones constantes o constantes. Por lo tanto, debido a que char solo puede ser una expresión constante, es ilegal para todos los valores no integrales.


float es un poco más difícil de describir para la motivación, pero imagina a un miembro de la clase:

class MySpecialInt { public: constexpr MySpecialInt(const int & other) { } }; class MyClass { static const MySpecialInt a = 5; // error static constexpr MySpecialInt b = 5; // okay };

a en este escenario podría tener alguna construcción no trivial que potencialmente viole (o al menos complique gravemente) la regla de una definición. Debido a que constexpr ha garantizado propiedades de tiempo de compilación restrictivas, el constructor de copia de b también debe ser constexpr y, por lo tanto, se le garantiza que devolverá un valor bien definido en tiempo de compilación (y NO violará la regla de una definición)

Creo que el motivo por el que float muestra este comportamiento es solo por razones heredadas, ya que float nunca se ha inicializado tradicionalmente de esta manera ("porque la norma lo dice"), por lo que capturaron la inicialización de miembros constexpr const bajo el paraguas de constexpr .