Inicializando miembro const dentro de la declaración de clase en C ++

80

En PHP y C #, las constantes se pueden inicializar a medida que se declaran:

class Calendar3
{
    const int value1 = 12;
    const double value2 = 0.001;
}

Tengo la siguiente declaración de C ++ de un functor que se usa con otra clase para comparar dos vectores matemáticos:

struct equal_vec
{
    bool operator() (const Vector3D& a, const Vector3D& b) const
    {
        Vector3D dist = b - a;
        return ( dist.length2() <= tolerance );
    }

    static const float tolerance = 0.001;
};

Este código compilado sin problemas con g ++. Ahora, en modo C ++ 0x (-std = c ++ 0x), el compilador g ++ genera un mensaje de error:

error: 'constexpr' necesario para la inicialización en clase del miembro de datos estáticos 'tolerancia' de tipo no integral

Sé que puedo definir e inicializar este static constmiembro fuera de la definición de clase. Además, un miembro de datos constantes no estáticos se puede inicializar en la lista de inicializadores de un constructor.

¿Pero hay alguna forma de inicializar una constante dentro de la declaración de clase como es posible en PHP o C #?

Actualizar

Usé la staticpalabra clave solo porque era posible inicializar tales constantes dentro de la declaración de clase en g ++. Solo necesito una forma de inicializar una constante en una declaración de clase, sin importar si se declaró como statico no.

ezpresso
fuente
5
I used static keyword just because it was possible to initialize such constants within the class declaration in g++. I just need a way to initialize a constant in a class declaration no matter if it declared as static or not.Esa es la forma incorrecta de decidir si un miembro debería serlo statico no. Nunca dejes que la pereza léxica decida la semántica de tu código.
Lightness Races in Orbit
That's the wrong way to decide whether a member should be static or not.No estoy de acuerdo Creo que eso no importa para los miembros constantes.
ezpresso
3
@expresso: Para nada. Puede inicializar un staticmiembro no constante con información específica de la instancia. El hecho de que haya decidido que su constante es una propiedad del tipo en lugar de una instancia específica es la razón para hacerlo static, no porque le apeteciera un atajo de escritura.
Lightness Races in Orbit
@lightless: Bueno, es posible, pero no veo ninguna razón para hacer uso de la inicialización de las mismas constantes específicas de la instancia con diferentes valores. ¡Solía ​​usar campos de clase no constantes para eso!
ezpresso
4
¿Por qué, si nunca cambian después de la instanciación de objetos? struct myType { const std::time_t instantiated; myType() : instantiated(std::time(0)) {} };Todo lo que puede ser const debe ser const; que se aplica tanto a los miembros como a los staticno staticmiembros.
Lightness Races in Orbit

Respuestas:

136

En C ++ 11, staticlos miembros que no son de datos, static constexprlos miembros de datos y static constlos miembros de datos de tipo integral o enumeración pueden inicializarse en la declaración de clase. p.ej

struct X {
    int i=5;
    const float f=3.12f;
    static const int j=42;
    static constexpr float g=9.5f;
};

En este caso, el constructor generado por el compilador inicializa el imiembro de todas las instancias de la clase y el miembro se inicializa en . El miembro de datos se inicializa en y el miembro de datos se inicializa en .X5f3.12static constj42static constexprg9.5

Dado que floaty doubleno son de tipo integral o enumeración, dichos miembros deben ser constexpr, o no static, para que se permita el inicializador en la definición de clase.

Antes de C ++ 11, solo static constlos miembros de datos de tipo integral o enumeración podían tener inicializadores en la definición de clase.

Anthony Williams
fuente
¿Existe un enlace a la parte del estándar donde se especificó esto? Acabo de comenzar a usarlo (aunque compilando como C ++ 14) y estoy encantado de que funcione, ya que puedo ahorrar mucho tiempo al hacer que el compilador lo haga por mí. Sin embargo, hasta que leí tu respuesta, ¡no estaba seguro de si debería estar funcionando! Una palabra oficial ayudaría a mi confianza, jaja. Fwiw lo tengo trabajando con miembros como char const n[3]{'a', 'b', 'c'};también.
underscore_d
Me pregunto por qué static const int, pero static constexpr float? ¿qué significa float and double are not of integral or enumeration type?
mrgloom
@mrgloom: sí, float y double no lo son. Si el tipo de datos se puede traducir a un número entero, entonces es un tipo integral.
ppadhy
45

La inicialización de variables miembro estáticas distintas de los tipos const int no es C ++ estándar anterior a C ++ 11. El compilador gcc no le advertirá sobre esto (y, no obstante, producirá código útil) a menos que especifique la -pedanticopción. A continuación, debería obtener un error similar a:

const.cpp:3:36: error: floating-point literal cannot appear in a constant-expression
const.cpp:3:36: warning: ISO C++ forbids initialization of member constant ‘tolerance’ of non-integral type ‘const float’ [-pedantic]

La razón de esto es que el estándar C ++ no especifica cómo se debe implementar el punto flotante y se deja en manos del procesador. Para sortear esta y otras limitaciones constexprse introdujeron.

Florian
fuente
14

Si. Simplemente agregue la constexprpalabra clave como dice el error.

StilesCrisis
fuente
1
¿Quizás no quiere requerir C ++ 11 para su proyecto?
Marc Mutz - mmutz
6
La cuestión que menciona fue introducido debido al hecho de que trató de compilar como C ++ 11, es decir que le gustaría soporte para C ++ 11 :)
Unknown1987
1

Si solo lo necesita en un método, puede declararlo localmente estático:

struct equal_vec
{
    bool operator() (const Vector3D& a, const Vector3D& b) const
    {
        static const float tolerance = 0.001f;
        Vector3D dist = b - a;
        return ( dist.length2() <= tolerance );
    }
};
Peter Wood
fuente
1

Me encontré con problemas reales con esto, porque necesito el mismo código para compilar con diferentes versiones de g ++ (el compilador GNU C ++). Entonces tuve que usar una macro para ver qué versión del compilador se estaba usando, y luego actuar en consecuencia, así

#if __GNUC__ > 5
 #define GNU_CONST_STATIC_FLOAT_DECLARATION constexpr
#else
 #define GNU_CONST_STATIC_FLOAT_DECLARATION const
#endif

GNU_CONST_STATIC_FLOAT_DECLARATION static double yugeNum=5.0;

Esto usará 'const' para todo lo anterior a g ++ versión 6.0.0 y luego usará 'constexpr' para g ++ versión 6.0.0 y posteriores. Esa es una suposición de la versión donde se produce el cambio, porque francamente no me di cuenta de esto hasta la versión 6.2.1 de g ++. Para hacerlo bien, puede que tenga que mirar la versión menor y el número de parche de g ++, así que consulte

https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html

para obtener detalles sobre las macros disponibles.

Con gnu, también podría seguir usando 'const' en todas partes y luego compilar con la -fpermissivebandera, pero eso da advertencias y me gusta que mis cosas se compilen limpiamente.

No es genial, porque es específico de los compiladores gnu, pero sospecho que podrías hacer algo similar con otros compiladores.

nilesOien
fuente