2011-04-01 24 views
14

Estoy sorprendido por el error de vinculador cuando se utiliza el siguiente código:"const int estática" provoca error al vincular (undefined-referencia)

// static_const.cpp -- complete code 
#include <vector> 

struct Elem { 
    static const int value = 0; 
}; 

int main(int argc, char *argv[]) { 
    std::vector<Elem> v(1); 
    std::vector<Elem>::iterator it; 

    it = v.begin(); 
    return it->value; 
} 

Sin embargo, esto no funciona cuando se enlaza - de alguna manera tiene que tener una símbolo para el "valor" de const estático.

$ g++ static_const.cpp 
/tmp/ccZTyfe7.o: In function `main': 
static_const.cpp:(.text+0x8e): undefined reference to `Elem::value' 
collect2: ld returned 1 exit status 

Por cierto, esto compila bien con -O1 o mejor; pero aún falla para casos más complicados. Estoy usando gcc versión 4.4.4 20100726 (Red Hat 4.4.4-13).

Alguna idea de lo que podría estar mal con mi código?

+1

posible duplicado de [Símbolos indefinidos raros de constantes estáticas dentro de una estructura/clase] (http://stackoverflow.com/questions/4891067/weird-unfined-symbols-of-static-constants-inside-a-struct- clase) – karlphillip

+0

¡Gracias por el útil enlace! También muestra una solución alternativa, 'struct Elem {enum {value = 0}; } ', que parece bastante atractivo. – hrr

+0

posible duplicado de [C++ - definición de miembros enteros const enteros en la definición de clase] (http://stackoverflow.com/questions/3025997/c-defining-static-const-integer-members-in-class-definition) – ks1322

Respuesta

8

Si desea inicializar el interior de la estructura , puede hacerlo también:

struct Elem { 
    static const int value = 0; 
}; 

const int Elem::value; 
+0

Esto es ¡guay! Funciona. No tiene sentido no inicializarlo en la estructura (clase) si puede (conocer el valor en ese punto) porque de lo contrario se perderían todas las optimizaciones posibles. (es decir, pasar 0 directamente en lugar de cargar 0 desde alguna dirección de memoria.) –

+0

Si esto es realmente const, existe la alternativa del "enum hack", ver: http://stackoverflow.com/questions/4891067/weird-undefined -symbols-of-static-constants-inside-a-struct-class Los problemas también se describen allí: antes de que C++ 11 enumeraciones no tengan un "tipo" decente como 'int', entonces std :: min, std :: make_pair etc. no adivinarán su argumento de plantilla ... –

5

intentar escribir como

struct Elem { 
    static const int value; 
}; 

const int Elem::value = 0; 

etc 

.

+0

Desafortunadamente, esto no funcionará, cuando el valor sea una etiqueta en la declaración de cambio. – Petr

+0

@Petr Lo siento, no estoy seguro de qué decir allí. No he usado C++ en algunos años, así que no sé si eso es peculiar de la 'const estática' o qué. – jonsca

+1

Debería haberme dejado más claro. Traté de señalarle a cualquiera que leyera tu respuesta que, aunque perfectamente correcto, hay casos en que esto no funciona. Por ejemplo, si quiere usar el 'valor' como una etiqueta en el interruptor, como' cambiar (x) {valor de caso: romper; } ' – Petr

2

static Se supone que los miembros de la clase se definen fuera de la clase (declarados en el interior, definidos en el exterior) en una sola unidad de compilación.

No recuerdo cómo eso interactúa con la inicialización en línea de los miembros integrales estáticos const.

+1

vea http://stackoverflow.com/questions/1312241/using-a-static-const-int-in-a-struct-class/1312267#1312267 –

+1

No hay una regla especial para el caso donde la declaración tiene un inicializador --- de acuerdo con el estándar, el objeto declarado debe definirse en algún lugar si se utiliza potencialmente. En la práctica, la mayoría de los compiladores son defectuosos en este aspecto, y solo generarán un error para ciertos usos, no para otros, si falta la definición. (Los usos nunca se especifican, y generalmente varían según el nivel de optimización.) –

1

¿Por qué no hacer esto?

return Elem::value; 

Pero la respuesta es que está asignando un valor en la declaración. Se supone que esto funciona para tipos básicos como int, y solo se requiere para tipos complejos (es decir, clases, como si tuviera una cadena en lugar de int). Lo que he encontrado en la práctica es que esto es impredecible dependiendo de qué versión de qué compilador estés usando. Y, como descubriste, qué nivel de optimización.

+0

Sin embargo, eso no resuelve el problema de inicialización. Además, golpear o perder a menudo significa un comportamiento indefinido. – jonsca

+4

Eso es solo evitar la pregunta que está haciendo. –

+0

En realidad, lo respondí. Dije que lo que él está haciendo debería funcionar (es decir, no hay nada de malo), pero en la práctica no siempre funciona. Parece haber encontrado uno de esos casos de "no funciona". –

2

También vea this post: esencialmente, el problema es que de alguna manera el compilador termina expandiendo su código para tomar la dirección de Elem :: value.

Cuestiones relacionadas