2010-06-11 11 views
79

Mi comprensión es que C++ permite que los miembros static const se definan dentro de una clase siempre que sea un tipo entero.Definición de miembros enteros const enteros en la definición de clase

¿Por qué, entonces, el siguiente código me da un error de enlazador?

#include <algorithm> 
#include <iostream> 

class test 
{ 
public: 
    static const int N = 10; 
}; 

int main() 
{ 
    std::cout << test::N << "\n"; 
    std::min(9, test::N); 
} 

El error que consigo es:

test.cpp:(.text+0x130): undefined reference to `test::N' 
collect2: ld returned 1 exit status 

Curiosamente, si comento hacia fuera la llamada a std :: min, el código se compila y enlaces muy bien (a pesar de que la prueba :: N es también referenciado en la línea anterior).

¿Alguna idea de qué está pasando?

Mi compilador es gcc 4.4 en Linux.

+3

Funciona bien en Visual Studio 2010. – Puppy

+1

Este error exacto se explica en https://gcc.gnu.org/wiki/VerboseDiagnostics#missing_static_const_definition –

+0

En el caso particular de 'char', puede definirlo en su lugar como' constexpr estático const char & N = "n" [0]; '. Tenga en cuenta el 'y'. Supongo que esto funciona porque las cadenas literales se definen automáticamente. Sin embargo, estoy un poco preocupado por esto: podría comportarse de manera extraña en un archivo de encabezado entre diferentes unidades de traducción, ya que la cadena probablemente estará en varias direcciones diferentes. –

Respuesta

49

Mi comprensión es que C++ permite que los miembros estáticos de una constante se definan dentro de una clase, siempre que sea un tipo de entero.

Usted es un poco correcto. Se le permite inicializar integrales estást const en la declaración de clase, pero eso no es una definición.

http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr038.htm

Curiosamente, si comento hacia fuera la llamada a std :: min, el código se compila y enlaces muy bien (a pesar de que la prueba :: N también se hace referencia en la línea anterior).

¿Alguna idea de qué está pasando?

std :: min toma sus parámetros por const reference. Si les tomó por valor, no tendrían este problema, pero dado que necesitan una referencia, también necesitan una definición.

Aquí es capítulo/verso:

9.4.2/4 - Si un miembro de datos static es de tipo integral o constconst enumeración, su declaración en la definición de clase puede especificar un constante inicializador cuales 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 utiliza en el programa y la definición de ámbito de espacio de nombres no contendrá un inicializador .

Consulte la respuesta de Chu para una posible solución.

+0

Ya veo, eso es interesante.En ese caso, ¿cuál es la diferencia entre proporcionar el valor en el punto de declaración versus proporcionar el valor en el punto de definición? ¿Cuál se recomienda? – HighCommander4

+0

Bueno, creo que puedes escapar sin una definición, siempre y cuando nunca "uses" la variable. Si solo lo usa como parte de una expresión constante, entonces la variable nunca se usa. De lo contrario, no parece haber una gran diferencia además de poder ver el valor en el encabezado, que puede ser o no lo que usted desea. –

+1

La respuesta breve es estática const x = 1; es un valor r pero no un valor. El valor está disponible como una constante en el tiempo de compilación (puede acotar una matriz con él) const y; [sin inicializador] se debe definir en un archivo cpp y se puede usar como valor r o valor l. –

10

No solo int. Pero no puede definir el valor en la declaración de clase. Si usted tiene:

class classname 
{ 
    public: 
     static int const N; 
} 

en el archivo .h, entonces debe tener:

int const classname::N = 10; 

en el archivo .cpp.

+2

Soy consciente de que puedes * declarar * una variable de cualquier tipo dentro de la declaración de clase. Dije que pensaba que las constantes constantes estáticas también podían * definirse * dentro de la declaración de clase. ¿No es este el caso? Si no, ¿por qué el compilador no da un error en la línea donde trato de definirlo dentro de la clase? Además, ¿por qué la línea std :: cout no causa un error de enlazador, pero la línea std :: min sí lo hace? – HighCommander4

+0

No, no se pueden definir miembros estáticos en la declaración de clase porque la inicialización emite código. A diferencia de una función en línea que también emite código, una definición estática es globalmente única. –

+0

@ HighCommander4: puede proporcionar un inicializador para el miembro integral 'static const' en la definición de clase. Pero todavía * no define * ese miembro. Ver la respuesta de Noah Roberts para más detalles. – AnT

22

Otra manera de hacer esto, de los tipos enteros de todos modos, es definir constantes como las enumeraciones de la clase: ejemplo

class test 
{ 
public: 
    enum { N = 10 }; 
}; 
+2

Y esto probablemente resolvería el problema. Cuando N se usa como un parámetro para min() hará que se cree un temporal en lugar de intentar referirse a una variable supuestamente existente. –

37

Bjarne de BS in his C++ FAQ sugiere estás en lo correcto, y sólo necesita una definición si se toma el dirección.

class AE { 
    // ... 
public: 
    static const int c6 = 7; 
    static const int c7 = 31; 
}; 

const int AE::c7; // definition 

int f() 
{ 
    const int* p1 = &AE::c6; // error: c6 not an lvalue 
    const int* p2 = &AE::c7; // ok 
    // ... 
} 

Dice "Usted puede tomar la dirección de un miembro estático si (y sólo si) tiene una definición fuera de clase". Lo que sugiere que funcionaría de otra manera. Tal vez su función mínima invoca direcciones de alguna manera detrás de la escena.

+0

'std :: min' toma sus parámetros por referencia, por lo que se requiere una definición. – Rakete1111

2

C++ permite a los miembros static const a ser definidas dentro de una clase

No, 3.1 § 2 dice:

Una declaración es una definición menos se declara una función sin especificar el cuerpo de la función (8.4), contiene el especificador externo (7.1.1) o una especificación de enlace (7.5) y ni un inicializador ni un cuerpo funcional, declara un miembro de datos estáticos en una definición de clase (9.4), es una declaración de nombre de clase (9.1), es una declaración enum opaco (7.2), o es una declaración typedef (7.1.3), una declaración using (7.3.3), o una directiva de uso (7.3.4).

8

Ésta es otra manera de solucionar el problema: (. Creo que la respuesta de Eddie el Loco describe correctamente qué existe el problema)

std::min(9, int(test::N)); 

+4

o incluso 'std :: min (9, + test :: N);' – Orient

+0

Aquí está la gran pregunta: ¿todo esto es óptimo? No sé ustedes, chicos, pero mi gran atracción para saltearse la definición es que no debería ocupar ningún espacio en el uso de la constante estática. – Opux

2

A partir de C++ 11 que puede (y usted quiere) a utilizar:

static constexpr int N = 10;

que no requiere que usted para definir los contras tant en un archivo .cpp.

Cuestiones relacionadas