Referencia indefinida a miembro de clase estática

7 minutos de lectura

Referencia indefinida a miembro de clase estatica
Pawel Piatkowski

¿Alguien puede explicar por qué el siguiente código no se compila? Al menos en g ++ 4.2.4.

Y más interesante, ¿por qué se compilará cuando lance MEMBER a int?

#include <vector>

class Foo {  
public:  
    static const int MEMBER = 1;  
};

int main(){  
    vector<int> v;  
    v.push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}

  • Edité la pregunta para sangrar el código con cuatro espacios en lugar de usar

     

    . Esto significa que los corchetes angulares no se interpretan como HTML.

    –Steve Jessop

    7 de noviembre de 2008 a las 19:03

  • stackoverflow.com/questions/16284629/… Puede consultar esta pregunta.

    – Aqeel Raza

    3 de octubre de 2018 a las 0:18

  • Desde C ++ 17 no hay necesidad de una definición adicional, vea mi respuesta a continuación.

    – Quimby

    10 de marzo de 2021 a las 17:23

Referencia indefinida a miembro de clase estatica
dibujó el salón

En realidad, debe definir el miembro estático en algún lugar (después de la definición de clase). Prueba esto:

class Foo { /* ... */ };

const int Foo::MEMBER;

int main() { /* ... */ }

Eso debería deshacerse de la referencia indefinida.

  • Buen punto, la inicialización de enteros constantes estáticos en línea crea una constante entera de alcance de la que no puede tomar la dirección, y el vector toma un parámetro de referencia.

    – Evan Terán

    7 de noviembre de 2008 a las 18:08

  • Esta respuesta solo aborda la primera parte de la pregunta. La segunda parte es mucho más interesante: ¿Por qué agregar un molde NOP hace que funcione sin requerir la declaración externa?

    –Brent Bradburn

    1 de febrero de 2011 a las 0:48


  • Acabo de pasar un buen rato averiguando que si la definición de clase está en un archivo de encabezado, entonces la asignación de la variable estática debería estar en el archivo de implementación, no en el encabezado.

    – Shanet

    14 de julio de 2012 a las 3:06

  • @shanet: Muy buen punto, ¡debería haberlo mencionado en mi respuesta!

    – Drew Pasillo

    14 de julio de 2012 a las 3:10

  • Pero si lo declaro constante, ¿no me es posible cambiar el valor de esa variable?

    – Namratha

    27 de noviembre de 2012 a las 4:57

Referencia indefinida a miembro de clase estatica
douglas mayle

El problema surge debido a un choque interesante entre las nuevas funciones de C++ y lo que está tratando de hacer. Primero, echemos un vistazo a la push_back firma:

void push_back(const T&)

Está esperando una referencia a un objeto de tipo T. Bajo el antiguo sistema de inicialización, tal miembro existe. Por ejemplo, el siguiente código se compila perfectamente:

#include <vector>

class Foo {
public:
    static const int MEMBER;
};

const int Foo::MEMBER = 1; 

int main(){
    std::vector<int> v;
    v.push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}

Esto se debe a que hay un objeto real en algún lugar que tiene ese valor almacenado. Sin embargo, si cambia al nuevo método de especificar miembros constantes estáticos, como lo hizo anteriormente, Foo::MEMBER ya no es un objeto. Es una constante, algo similar a:

#define MEMBER 1

Pero sin los dolores de cabeza de una macro de preprocesador (y con seguridad de tipo). Eso significa que el vector, que espera una referencia, no puede obtenerla.

  • gracias, eso ayudó… eso podría calificar para stackoverflow.com/questions/1995113/strangest-language-feature si aún no está allí…

    – André Holzner

    3 de diciembre de 2010 a las 13:45

  • También vale la pena señalar que MSVC acepta la versión no emitida sin quejas.

    – porges

    26 de junio de 2012 a las 23:49

  • -1: Esto simplemente no es cierto. Todavía se supone que debe definir miembros estáticos inicializados en línea, cuando están odr-usado algun lado. Que las optimizaciones del compilador puedan eliminar el error del enlazador no cambia eso. En este caso, su conversión de lvalue a rvalue (gracias a la (int) cast) ocurre en la unidad de traducción con perfecta visibilidad de la constante, y la Foo::MEMBER ya no es odr-usado. Esto contrasta con la primera llamada de función, donde se pasa una referencia y se evalúa en otro lugar.

    – Carreras de ligereza en órbita

    06/10/2014 a las 22:12


  • Qué pasa void push_back( const T& value );? const&‘s puede enlazar con rvalues.

    – Kostas

    24 de febrero de 2020 a las 21:38

1647637989 376 Referencia indefinida a miembro de clase estatica
ricardo corden

El estándar C++ requiere una definición para su miembro const estático si la definición es necesaria de alguna manera.

La definición es obligatoria, por ejemplo, si se utiliza su dirección. push_back toma su parámetro por referencia constante, por lo que estrictamente el compilador necesita la dirección de su miembro y debe definirlo en el espacio de nombres.

Cuando lanza explícitamente la constante, está creando un temporal y es este temporal el que está vinculado a la referencia (bajo reglas especiales en el estándar).

Este es un caso realmente interesante, y de hecho creo que vale la pena plantear un problema para que se cambie el estándar para que tenga el mismo comportamiento para su miembro constante.

Aunque, de una manera extraña, esto podría verse como un uso legítimo del operador unario ‘+’. Básicamente el resultado de la unary + es un rvalue y, por lo tanto, se aplican las reglas para vincular los rvalues ​​a las referencias constantes y no usamos la dirección de nuestro miembro constante estático:

v.push_back( +Foo::MEMBER );

  • +1. Sí, ciertamente es extraño que para un objeto x de tipo T, la expresión “(T) x” se pueda usar para vincular una referencia constante mientras que la simple “x” no puede. ¡Me encanta tu observación sobre “unario +”! Quién hubiera pensado que ese pobre y pequeño “unario +” en realidad tenía un uso… 🙂

    – j_random_hacker

    29 de mayo de 2009 a las 10:38

  • Pensando en el caso general… ¿Hay algún otro tipo de objeto en C++ que tenga la propiedad de que (1) se puede usar como un valor l solo si se ha definido pero (2) se puede convertir en un valor r sin ser definido?

    – j_random_hacker

    29 de mayo de 2009 a las 10:51

  • Buena pregunta, y al menos por el momento no puedo pensar en ningún otro ejemplo. Probablemente esto solo esté aquí porque el comité en su mayoría solo estaba reutilizando la sintaxis existente.

    -Richard Corden

    29 de mayo de 2009 a las 12:47

  • @RichardCorden: ¿cómo resuelve eso unario +?

    – PELIGRO DE SANGRE

    28 de junio de 2018 a las 15:10

  • @Blood-HaZaRd: antes de que rvalue haga referencia a la única sobrecarga de push_back era un const &. El uso directo del miembro resultó en que el miembro estuviera vinculado a la referencia, lo que requería que tuviera una dirección. Sin embargo, agregando el + crea un temporal con el valor del miembro. La referencia luego se une a ese temporal en lugar de requerir que el miembro tenga una dirección.

    -Richard Corden

    13 de julio de 2018 a las 10:48

1647637990 346 Referencia indefinida a miembro de clase estatica
iso9660

Aaa.h

class Aaa {

protected:

    static Aaa *defaultAaa;

};

Aaa.cpp

// You must define an actual variable in your program for the static members of the classes

static Aaa *Aaa::defaultAaa;

En C ++ 17, hay una solución más fácil usando inline variables:

struct Foo{
    inline static int member;
};

Esta es una definición de member, no sólo su declaración. De forma similar a las funciones en línea, múltiples definiciones idénticas en diferentes unidades de traducción no infringen la ODR. Ya no es necesario elegir un archivo .cpp favorito para la definición.

1647637990 641 Referencia indefinida a miembro de clase estatica
Bagazo

Solo alguna información adicional:

C++ permite “definir” tipos estáticos constantes de tipos integrales y de enumeración como miembros de clase. Pero esto en realidad no es una definición, solo un “marcador de inicialización”

Aún debe escribir una definición de su miembro fuera de la clase.

9.4.2/4 – Si un miembro de datos estáticos es del tipo const integral o const enumeration, su declaración en la definición de clase puede especificar un inicializador constante que será una expresión constante integral (5.19). En ese caso, el miembro puede aparecer en expresiones integrales constantes. El miembro aún se definirá en un ámbito de espacio de nombres si se utiliza en el programa y la definición del ámbito de espacio de nombres no contendrá un inicializador.

1647637990 572 Referencia indefinida a miembro de clase estatica
pablo tomblin

No tengo idea de por qué funciona el elenco, pero Foo::MEMBER no se asigna hasta la primera vez que se carga Foo, y dado que nunca lo carga, nunca se asigna. Si tuviera una referencia a un Foo en alguna parte, probablemente funcionaría.

  • Creo que estás respondiendo tu propia pregunta: el elenco funciona porque crea una referencia (temporal).

    – Jaap Versteegh

    30 de noviembre de 2011 a las 15:43

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad