¿Por qué no puedo inicializar un static
miembro o static
matriz 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
static
miembros de datos en clase? - ¿Por qué no puedo inicializar
static
matrices en clase, incluso laconst
matriz?
Respuestas:
¿Por qué no puedo inicializar
static
miembros 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
a
se 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 const
permiten 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 const
nú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 const
valores 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
&member
volverí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
0123
en el brazo y3210
en 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::a
que 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 inline
ostatic const
remediar esto.static inline
solo 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.const
El alcance del archivo hace que el compilador nunca emita un símbolo porque siempre se sustituye inmediatamente en el código a menos queextern
se 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 b
ostatic 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 serconstexpr
oinline
para 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. Loconstexpr
convierte en una definición ). Si usaconstexpr
y especifica un constructor predeterminado, entonces el constructor deberá serconstexpr
Un 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