2012-02-09 22 views
7

Si tuviera que hacer esto¿Por qué se puede inicializar una línea variables static const pero no una estática normal (C++)

class Gone 
{ 
    public: 
    static const int a = 3; 
} 

funciona pero si hacer

class Gone 
{ 
    public: 
    static int a = 3; 
} 

que da un error de compilación . Ahora sé por qué el segundo no funciona, simplemente no sé por qué lo hace el primero.

Gracias de antemano.

+0

posible duplicado de [Definición de miembros estáticos en C++] (http://stackoverflow.com/questions/3536372/defining-static-members-in-c) –

+0

Esto podría ayudar: http://stackoverflow.com/questions/370283/why-cant-i-have-a-non-integral-static-const-member-in-a-class –

+0

@Uku Loskit Realmente no respondieron la segunda parte, todavía no entiendo la lógica Detrás de eso. – rubixibuc

Respuesta

6

Este truco solo funciona para expresiones constantes de tiempo de compilación. Consideremos el siguiente ejemplo:

#include <iostream> 

class Foo { 
public: 
    static const int bar = 0; 
}; 

int main() 
{ 
    std::cout << Foo::bar << endl; 
} 

Funciona muy bien, porque el compilador sabe que Foo::bar es 0 y nunca cambia. Por lo tanto, optimiza todo el asunto.

Sin embargo, todo el asunto se rompe una vez que tome la dirección de esa variable como esto:

int main() 
{ 
    std::cout << Foo::bar << " (" << &Foo::bar << ")" << std::endl; 
} 

Enlazador le envía a fijar el programa debido a las constantes de tiempo de compilación no tienen direcciones.

Ahora, el segundo caso en su ejemplo no funciona simplemente porque una variable no constante no puede ser una expresión constante en tiempo de compilación. Por lo tanto, debe definirlo en algún lugar y no puede asignar ningún valor en la inicialización.

C++ 11, por cierto, tiene constexpr. Puede consultar el wiki de Generalized constant expressions (o el estándar C++ 11 :-)) para obtener más información.

Además, tenga cuidado: con algunas cadenas de herramientas nunca podrá vincular el programa como se indica en el primer ejemplo cuando las optimizaciones están desactivadas, incluso si nunca toma una dirección de esas variables. Creo que hay una macro BOOST_STATIC_CONSTANT en Boost para evitar este problema (aunque no estoy seguro de si funciona, porque creo que he visto errores de vinculación con algunos gcc antiguos incluso con esa macro).

0

Me parece recordar que originalmente (ARM) no estaba permitido, y solíamos usar enum para definir constantes en las declaraciones de clase.

El caso const se introdujo explícitamente a fin de apoyar la disponibilidad del valor en las cabeceras para su uso en expresiones constantes, tales como tamaños de matriz.

Yo creo (y por favor comentar si lo he entendido mal) que estrictamente usted todavía tiene que definir el valor:

const int Gone::a;

a cumplir con la regla Una definición. Sin embargo, en la práctica, puede encontrarse con que el compilador optimiza elimina la necesidad de una dirección para Gone::a y escapar sin ella.

si toma:

const int* b = &Gone::a;

entonces usted puede encontrar usted no necesita la definición.

de ver bandera, $ 9.4.2:

ISO 1998:

"4 Si un miembro de datos estáticos const es integral o const tipo de enumeración , su declaración en la definición de clase puede especificar un inicializador de que debe ser una expresión constante integral (5.19). En ese caso, el miembro puede aparecer en expresiones de constante integral dentro de su alcance. El miembro se definirá en un ámbito de espacio de nombres si se usa en el programa y th La definición del alcance del espacio de nombres e no debe contener un inicializador. "

Proyecto para C++ 11:

"3 Si un miembro de datos estático es de tipo literal efectiva const, su declaración en la definición de clase puede especificar una constante inicializador corsé-o -equal-initializer con una cláusula initializer que es una expresión de constante integral . Un miembro de datos estáticos del tipo literal efectivo se puede declarar en la definición de clase con el especificador constexpr ; si es así, su declaración especificará una constante - inicializador corchete-o-igual-initi alizer con una cláusula de inicializador que es una expresión de constante integral. En ambos casos , el miembro puede aparecer en expresiones constantes integrales. El miembro todavía se definirá en un ámbito espacio de nombres si se utiliza en el programa y la definición del alcance del espacio de nombres no contendrá un inicializador ."

No estoy seguro del todo lo que esto cubre, pero creo esto significa que ahora podemos usar el mismo lenguaje para el punto y, posiblemente, los literales de cadena flotante

2

la declaración static const int es legal porque usted está declarando una constante, no una variable a no existe como una variable -.. el compilador es libre de optimizarlo, reemplazándolo con el valor declarado 3 en cualquier lugar una referencia a Gone::a aparece. C++ permite la inicialización estática en este caso restringido donde es una constante entera.

Puede encontrar más detalles, incluida una citación estándar ISO C++ here.

0

Una constante estática se define en la definición de clase, ya que todos los que usan el código necesitan saber el valor en tiempo de compilación, no el tiempo de enlace. Una estática ordinaria en realidad solo se declara en la definición de clase, pero se define una vez, en una unidad de traducción.

2

inicialización de variables que hay que hacer en el punto de definición, no es el punto de declaración en el caso general.Dentro de los corchetes de clase es suficiente con una declaracióny es necesario proporcionar una definiciónen una única unidad de traducción *:

// can be in multiple translation units (i.e. a header included in different .cpp's) 
struct test { 
    static int x; // declaration 
    static double d; // declaration 
}; 
// in a single translation unit in your program (i.e. a single .cpp file) 
int test::x = 5;  // definition, can have initialization 
double test::d = 5.0; // definition 

Dicho esto, hay una excepción para static constantes integrales (y solo constantes integrales) donde puede proporcionar el valor de la constante en la declaración . El motivo de la excepción es que puede usarse como una constante en tiempo de compilación (es decir, para definir el tamaño de una matriz), y eso solo es posible si el compilador ve el valor de la constante en todas las unidades de traducción donde se necesita

struct test { 
    static const int x = 5; // declaration with initialization 
}; 
const int test::x;   // definition, cannot have initialization 

Volviendo a la pregunta original:

  • ¿Por qué no permitió enteros no const?
  • porque la inicialización ocurre en la definición y no declaración.
  • ¿Por qué está permitido para constantes integrales?
  • para que pueda ser utilizado como un tiempo de compilación constante en todas las unidades de traducción

* Las reglas actuales requieren la definición cada vez que el atributo de miembro es utiliza en el programa . Ahora, la definición de utilizada es un poco complicada en C++ 03, ya que puede no ser tan intuitiva, por ejemplo, el uso de esa constante como rvalue no constituye uso según el estándar. En C++ 11 el término usado ha sido reemplazado por odr-used en un intento por evitar confusiones.

+0

¿Debe proporcionar una definición para las constantes integrales, o es suficiente la declaración? – rubixibuc

+0

¿qué se entiende por atributo de miembro? Entonces, ¿cuándo lo usaría alguna vez y necesitaría la definición? ¿Tomaría la dirección de ella usarlo? – rubixibuc

+0

@rubixibuc: Debería haber usado * variable de miembro * o * variable de miembro estática * en lugar de * atributo de miembro *. Con respecto a la primera pregunta: sí, debe proporcionar una definición si se usa * odr *. ¿Cuándo será * odr-used *? Si lo usa como * lvalue * (que incluye obtener la dirección o vincular una referencia a ella). Tendría que verificar si hay otros casos, pero esos son los más comunes (tal vez solo los únicos). Ejemplo: 'void f (const int &); ... f (test :: x);' requiere 'test :: x' para definirse. –

Cuestiones relacionadas