¿Por qué no puedo inicializar un miembro estático no const o una matriz estática en la clase?

¿Por qué no puedo inicializar un miembro static no const o una matriz static en una clase?

 class A { static const int a = 3; static int b = 3; static const int c[2] = { 1, 2 }; static int d[2] = { 1, 2 }; }; int main() { A a; return 0; } 

el comstackdor emite los siguientes errores:

 g++ main.cpp main.cpp:4:17: error: ISO C++ forbids in-class initialization of non-const static member 'b' main.cpp:5:26: error: a brace-enclosed initializer is not allowed here before '{' token main.cpp:5:33: error: invalid in-class initialization of static data member of non-integral type 'const int [2]' main.cpp:6:20: error: a brace-enclosed initializer is not allowed here before '{' token main.cpp:6:27: error: invalid in-class initialization of static data member of non-integral type 'int [2]' 

Tengo dos preguntas:

  1. ¿Por qué no puedo inicializar static miembros de datos static en la clase?
  2. ¿Por qué no puedo inicializar matrices static en clase, incluso la matriz const ?

¿Por qué no puedo inicializar static miembros de datos static en la clase?

El estándar de C ++ permite inicializar solo los tipos de enteros constantes estáticos o de enumeración dentro de la clase. Esta es la razón por la que se permite inicializar a a mientras que otros no.

Referencia:
C ++ 03 9.4.2 Miembros de datos estáticos
§4

Si un miembro de datos estático es de tipo const integral o const enumeration, su statement en la definición de clase puede especificar un constante-inicializador que debe ser una expresión de constante integral (5.19). En ese caso, el miembro puede aparecer en expresiones constantes integrales. El miembro aún se definirá en un ámbito de espacio de nombres si se usa en el progtwig y la definición del scope del espacio de nombres no contendrá un inicializador.

¿Qué son los tipos integrales?

C ++ 03 3.9.1 Tipos fundamentales
§7

Los tipos bool, char, wchar_t y los tipos enteros con signo y sin signo se denominan colectivamente tipos integrales.43) Un sinónimo para tipo integral es tipo entero.

Nota:

43) Por lo tanto, las enumeraciones (7.2) no son integrales; sin embargo, las enumeraciones pueden promocionarse a int, unsigned int, long o unsigned long, como se especifica en 4.5.

Solución:

Podría usar el truco de enum para inicializar una matriz dentro de su definición de clase.

 class A { static const int a = 3; enum { arrsize = 2 }; static const int c[arrsize] = { 1, 2 }; }; 

¿Por qué el estándar no permite esto?

Bjarne explica esto acertadamente aquí :

Por lo general, una clase se declara en un archivo de cabecera y, por lo general, un archivo de cabecera se incluye en muchas unidades de traducción. Sin embargo, para evitar reglas de enlazador complicadas, C ++ requiere que cada objeto tenga una definición única. Esa regla se rompería si C ++ permitiera la definición dentro de la clase de las entidades que debían almacenarse en la memoria como objetos.

¿Por qué solo se permiten los tipos y entos integrales static const Inicialización en clase?

La respuesta está escondida en la cita de Bjarne, lea detenidamente,
“C ++ requiere que cada objeto tenga una definición única. Esa regla se rompería si C ++ permitiera la definición en clase de las entidades que debían almacenarse en la memoria como objetos”.

Tenga en cuenta que solo static const enteros static const se pueden tratar como constantes de tiempo de comstackción. El comstackdor sabe que el valor entero no cambiará en cualquier momento y por lo tanto puede aplicar su propia magia y aplicar optimizaciones, el comstackdor simplemente ingresa dichos miembros de la clase, es decir, ya no se almacenan en la memoria, ya que se elimina la necesidad de almacenarla en la memoria , da a tales variables la excepción a la regla mencionada por Bjarne.

Cabe señalar aquí que incluso si static const valores integrales de la static const pueden tener Inicialización en la clase, no se permite tomar la dirección de dichas variables. Uno puede tomar la dirección de un miembro estático si (y solo si) tiene una definición fuera de clase. Esto valida aún más el razonamiento anterior.

las enumeraciones se permiten porque los valores de un tipo enumerado se pueden usar donde se esperan los resultados. ver cita arriba


¿Cómo cambia esto en C ++ 11?

C ++ 11 relaja la restricción en cierta medida.

C ++ 11 9.4.2 Miembros de datos estáticos
§3

Si un miembro de datos estático es de tipo const literal, su statement en la definición de clase puede especificar un inicializador de llave o igual en el que cada cláusula de inicializador que sea una expresión de asignación sea ​​una expresión constante. Un miembro de datos estáticos de tipo literal se puede declarar en la definición de clase con el constexpr specifier; de ser así, su statement deberá especificar un inicializador de llave o igual en el que cada cláusula de inicializador que sea una expresión de asignación sea ​​una expresión constante. [Nota: en ambos casos, el miembro puede aparecer en expresiones constantes. -Finalizar nota] El miembro aún se definirá en un ámbito de espacio de nombres si se usa en el progtwig y la definición del scope del espacio de nombres no contendrá un inicializador.

Además, C ++ 11 permitirá (§12.6.2.8) que un miembro de datos no estático se inicialice donde se declara (en su clase). Esto significará mucha semántica de usuario fácil.

Tenga en cuenta que estas características aún no se han implementado en la última versión de gcc 4.7, por lo que aún puede obtener errores de comstackción.

Esto parece una reliquia de los viejos tiempos de los simples enlazadores. Puede usar variables estáticas en métodos estáticos como solución alternativa:

 // header.hxx #include  class Class { public: static std::vector & replacement_for_initialized_static_non_const_variable() { static std::vector Static {42, 0, 1900, 1998}; return Static; } }; int comstacktion_unit_a(); 

y

 // comstacktion_unit_a.cxx #include "header.hxx" int comstacktion_unit_a() { return Class::replacement_for_initialized_static_non_const_variable()[1]++; } 

y

 // main.cxx #include "header.hxx" #include  int main() { std::cout < < compilation_unit_a() << Class::replacement_for_initialized_static_non_const_variable()[1]++ << compilation_unit_a() << Class::replacement_for_initialized_static_non_const_variable()[1]++ << std::endl; } 

construir:

 g++ -std=gnu++0x -save-temps=obj -c comstacktion_unit_a.cxx g++ -std=gnu++0x -o main main.cxx comstacktion_unit_a.o 

correr:

 ./main 

El hecho de que esto funcione (consistentemente, incluso si la definición de la clase está incluida en diferentes unidades de comstackción), muestra que el enlazador actual (gcc 4.9.2) es realmente lo suficientemente inteligente.

Gracioso: Imprime 0123 en el arm y 3210 en x86.

Creo que es para evitar que mezcles declaraciones y definiciones. (Piense en los problemas que podrían ocurrir si incluye el archivo en varios lugares).

las variables estáticas son específicas de una clase. Los constructores inicializan los atributos ESPECIALMENTE para una instancia.