c++ - estaticos - ¿Por qué no puedo inicializar un miembro estático no const o una matriz estática en la clase?
miembros estaticos c++ (4)
¿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 declaración 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 programa y la definición del alcance 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 here :
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 compilación. El compilador sabe que el valor entero no cambiará en cualquier momento y por lo tanto puede aplicar su propia magia y aplicar optimizaciones, el compilador 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 declaración 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 declaración 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 programa y la definición del alcance 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 compilación.
¿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 compilador 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:
- ¿Por qué no puedo inicializar
static
miembros de datosstatic
en la clase? - ¿Por qué no puedo inicializar matrices
static
en clase, incluso la matrizconst
?
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).
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 <vector>
class Class {
public:
static std::vector<int> & replacement_for_initialized_static_non_const_variable() {
static std::vector<int> Static {42, 0, 1900, 1998};
return Static;
}
};
int compilation_unit_a();
y
// compilation_unit_a.cxx
#include "header.hxx"
int compilation_unit_a() {
return Class::replacement_for_initialized_static_non_const_variable()[1]++;
}
y
// main.cxx
#include "header.hxx"
#include <iostream>
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 compilation_unit_a.cxx
g++ -std=gnu++0x -o main main.cxx compilation_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 compilación), muestra que el enlazador actual (gcc 4.9.2) es realmente lo suficientemente inteligente.
Gracioso: Imprime 0123
en el brazo y 3210
en x86.
las variables estáticas son específicas de una clase. Los constructores inicializan los atributos ESPECIALMENTE para una instancia.