seguidores pegar para hashtags copiar conseguir c++ initialization definition static-members undefined-symbol

c++ - pegar - Extraños símbolos indefinidos de constantes estáticas dentro de una estructura/clase



hashtags en linkedin 2018 (5)

Definición necesaria

El código que ha proporcionado no es estándar. Si bien puede proporcionar inicializadores para miembros const const int directamente en la clase, aún necesita proporcionar definiciones separadas. Es extraño, un tipo de inesperado, pero se espera que lo escribas así:

#include <algorithm> struct Foo { static const int A = 1; static const int B = 2; }; const int Foo::A; const int Foo::B; int main() { return std::min(Foo::A, Foo::B); }

La cita del estándar se puede encontrar en una pregunta similar en const y en los especificadores estáticos en c ++

¿Por qué a veces el código "funciona" sin una definición?

En cuanto a por qué a menudo puede moverse incluso sin proporcionar la definición: si usa esos miembros solo en expresiones constantes, el compilador siempre los resolverá directamente y no habrá acceso para la resolución del vinculador. Es solo cuando lo usa de alguna manera que no puede ser manejado directamente por el compilador, y solo en tal caso, el enlazador detectará que el símbolo no está definido. Supongo que este es probablemente un error en el compilador de Visual Studio, pero dada la naturaleza del error, dudo que alguna vez se solucione.

Por qué su fuente cae en la categoría de "vinculador" es algo que no veo, uno tendría que analizar el estándar std :: min para entenderlo. Nota: cuando lo probé en línea con GCC , funcionó, no se detectó el error.

Alternativa: usar enum

Otra alternativa es utilizar enumeración. Esta versión también puede ser útil cuando golpea un compilador antiguo que no admite inicializadores constantes "en línea" estáticos (como fue Visual Studio 6). Sin embargo, tenga en cuenta que con std :: min está enfrentando otros problemas con enumeraciones y necesita usar una creación de instancias o conversión explícita, o tener A y B en una enumeración nombrada como en la respuesta de Nawaz :

struct Foo { enum {A = 1}; enum {B = 2}; }; int main() { return std::min<int>(Foo::A, Foo::B); }

Estándares

Nota: incluso las Preguntas frecuentes de Stroustrup C ++ se equivocan y no requieren la definición tan estrictamente como lo hace la norma:

Puede tomar la dirección de un miembro estático si (y solo si) tiene una definición fuera de clase

La definición es requerida por un estándar en 9.4.2:

C ++ 03 redacción:

El miembro aún se definirá en un ámbito de espacio de nombres si se usa en el programa y la definición del ámbito de espacio de nombres no contendrá un inicializador

La redacción de C ++ 11 de 9.4.2 es un poco diferente:

3 El miembro aún se definirá en el ámbito de un espacio de nombres si es odr-used (3.2) en el programa

3.2 dice lo siguiente sobre el uso de odr:

3 Una variable x cuyo nombre aparece como una expresión potencialmente evaluada ex es odr-used a menos que x sea un objeto que cumpla con los requisitos para aparecer en una expresión constante (5.19) y ex sea un elemento del conjunto de resultados potenciales de una expresión e, donde cualquiera de las conversiones lvalue a rvalue (4.1) se aplica a e, o e es una expresión de valor descartado (Cláusula 5).

4 Cada programa debe contener exactamente una de fi nición de cada función o variable no en línea que se utiliza en ese programa; no requiere diagnóstico

Debo admitir que no estoy seguro de cuáles son las implicaciones exactas de la redacción de C ++ 11, ya que no entiendo las reglas de uso de ODR.

O bien estoy muy cansado o algo extraño está sucediendo de lo que no estoy enterado, porque el código a continuación da como resultado símbolos indefinidos para Foo :: A y Foo :: B al vincular . Esto se minimiza tanto como podría en un proyecto más grande, pero muestra la esencia de lo que estoy viendo.

#include <algorithm> struct Foo { static const int A = 1; static const int B = 2; }; int main() { return std::min(Foo::A, Foo::B); }

Sin la plantilla de función std :: min funciona bien , es decir, simplemente devuelve Foo :: A. También está bien cuando se definen las entradas estáticas fuera de una clase / estructura (global en este caso simple). Sin embargo, tan pronto como están dentro de esta manera, el enlazador no puede encontrarlos.

¿Alguien puede explicar lo que está pasando?


Debe definir las constantes estáticas fuera de la definición de clase.

struct Foo { static const int A; static const int B; }; const int Foo::A = 1; const int Foo::B = 2;


Hay buenas respuestas aquí, pero una cosa adicional a tener en cuenta es que los parámetros de std::min() son referencias, que es lo que requiere las direcciones de las variables pasadas, y dado que esas variables no llegan al objeto Para la unidad de compilación, el enlazador no puede resolver sus direcciones.

Probablemente esté obteniendo esto en una compilación no optimizada, ¿correcto?

Apuesto a que no obtendrás esto con gcc si habilitas las optimizaciones. La llamada a std::min() se insertará y las referencias desaparecerán.

Además, si tuviera que asignar Foo::A y Foo::B a dos variables locales justo antes de la llamada a std::min() , este problema también desaparecerá.

Esto no es ideal, pero si no posee el código que define las variables que le están causando este problema, entonces esto es algo que puede considerar.


Si solo desea valores integrales, también puede definir la enum :

#include <algorithm> struct Foo { enum integrals { A = 1, B = 2} ; }; int main() { return std::min(Foo::A, Foo::B); }

Esto es más que suficiente. No se necesita declaración fuera de la clase!

Demostración en línea: http://www.ideone.com/oE9b5


Viendo que básicamente estás usando la estructura como un espacio de nombres, ¿por qué no solo usar el espacio de nombres?

#include <algorithm> namespace Foo { const int A = 1; const int B = 2; }; int main() { return std::min(Foo::A, Foo::B); }