¿Por qué no puedo inicializar un staticmiembro o staticmatriz no constante 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
staticmiembros de datos en clase? - ¿Por qué no puedo inicializar
staticmatrices en clase, incluso laconstmatriz?

Respuestas:
¿Por qué no puedo inicializar
staticmiembros de datos en clase?El estándar C ++ permite que solo se inicialicen tipos de enumeración o integrales constantes estáticas dentro de la clase. Esta es la razón por la que
ase permite la inicialización mientras que otras no.Referencia:
C ++ 03 9.4.2 Miembros de datos estáticos
§4
¿Qué son los tipos integrales?
C ++ 03 3.9.1 Tipos fundamentales
§7
Nota:
Solución alterna:
Puede usar el truco de la enumeración para inicializar una matriz dentro de la definición de su clase.
¿Por qué la Norma no lo permite?
Bjarne explica esto acertadamente aquí :
¿Por qué solo se
static constpermiten tipos integrales y enumeraciones en clase?La respuesta está oculta en la cita de Bjarne, léala atentamente,
"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 deben almacenarse en la memoria como objetos".
Tenga en cuenta que solo los
static constnúmeros enteros pueden tratarse como constantes de tiempo de compilación. El compilador sabe que el valor entero no cambiará en ningún momento y, por lo tanto, puede aplicar su propia magia y aplicar optimizaciones, el compilador simplemente integra dichos miembros de clase, es decir, ya no se almacenan en la memoria, ya que se elimina la necesidad de ser almacenados en la memoria. , otorga a tales variables la excepción a la regla mencionada por Bjarne.Vale la pena señalar aquí que incluso si los
static constvalores integrales pueden tener In-Class Initialization, no se permite tomar la dirección de tales variables. Se 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 están permitidas porque se pueden usar valores de un tipo enumerado donde se esperan ints. ver cita anterior
¿Cómo cambia esto en C ++ 11?
C ++ 11 relaja la restricción hasta cierto punto.
C ++ 11 9.4.2 Miembros de datos estáticos
§3
También, C ++ 11 será permitir (§12.6.2.8) un miembro de datos no estático para inicializar donde se declara (en su clase). Esto significará una semántica de usuario mucho más sencilla.
Tenga en cuenta que estas funciones aún no se han implementado en la última versión de gcc 4.7, por lo que es posible que aún obtenga errores de compilación.
fuente
&membervolvería?static const char*miembro.Esto parece una reliquia de los viejos tiempos de los enlazadores simples. Puede utilizar variables estáticas en métodos estáticos como solución:
y
y
construir:
correr:
El hecho de que esto funcione (de manera consistente, 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 lo suficientemente inteligente.
Divertido: imprime
0123en el brazo y3210en x86.fuente
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).
fuente
Es porque solo puede haber una definición de la
A::aque usan todas las unidades de traducción.Si se desempeñó
static int a = 3;en una clase en un encabezado incluido en todas las unidades de traducción, obtendría múltiples definiciones. Por lo tanto, la definición no fuera de línea de una estática se convierte forzosamente en un error del compilador.Usar
static inlineostatic constremediar esto.static inlinesolo concretiza el símbolo si se usa en la unidad de traducción y asegura que el enlazador solo selecciona y deja una copia si está definido en múltiples unidades de traducción debido a que está en un grupo comdat.constEl alcance del archivo hace que el compilador nunca emita un símbolo porque siempre se sustituye inmediatamente en el código a menos queexternse use, lo cual no está permitido en una clase.Una cosa a tener en cuenta es que
static inline int b;se trata como una definición, mientras questatic const int bostatic const A b;todavía se tratan como una declaración y deben definirse fuera de línea si no lo define dentro de la clase. Curiosamente,static constexpr A b;se trata como una definición, mientras questatic constexpr int b;es un error y debe tener un inicializador (esto se debe a que ahora se convierten en definiciones y, como cualquier definición const / constexpr en el alcance del archivo, requieren un inicializador que un int no tiene sino un tipo de clase lo hace porque tiene un implícito= A()cuando es una definición - clang permite esto, pero gcc requiere que se inicialice explícitamente o es un error. Esto no es un problema con inline en su lugar).static const A b = A();no está permitido y debe serconstexproinlinepara permitir un inicializador para un objeto estático con tipo de clase, es decir, hacer un miembro estático de tipo de clase más que una declaración. Entonces, sí en ciertas situacionesA a;no es lo mismo que inicializar explícitamenteA a = A();(la primera puede ser una declaración, pero si solo se permite una declaración para ese tipo, la última es un error. La última solo se puede usar en una definición. Loconstexprconvierte en una definición ). Si usaconstexpry especifica un constructor predeterminado, entonces el constructor deberá serconstexprUn miembro estático es una declaración de alcance de archivo simple
extern int A::a;(que solo se puede hacer en la clase y las definiciones fuera de línea deben hacer referencia a un miembro estático en una clase y deben ser definiciones y no pueden contener extern) mientras que un miembro no estático es parte de la definición de tipo completa de una clase y tienen las mismas reglas que las declaraciones de alcance de archivo sinextern. Son definiciones implícitas. También loint i[]; int i[5];es una redefinición, mientrasstatic int i[]; int A::i[5];que no lo es, pero a diferencia de 2 externos, el compilador aún detectará un miembro duplicado si lo hacestatic int i[]; static int i[5];en la clase.fuente
las variables estáticas son específicas de una clase. Los constructores inicializan los atributos ESPECIALMENTE para una instancia.
fuente