¿Alguien puede explicar por qué el siguiente código no se compilará? Al menos en g ++ 4.2.4.
Y más interesante, ¿por qué se compilará cuando envíe MIEMBRO 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;
}
Respuestas:
En realidad, debe definir el miembro estático en alguna parte (después de la definición de clase). Prueba esto:
Eso debería eliminar la referencia indefinida.
fuente
El problema surge debido a un interesante choque de nuevas características de C ++ y lo que estás tratando de hacer. Primero, echemos un vistazo a la
push_back
firma:Se espera una referencia a un objeto de tipo
T
. Bajo el antiguo sistema de inicialización, dicho miembro existe. Por ejemplo, el siguiente código compila muy bien:Esto se debe a que hay un objeto real en algún lugar que tiene ese valor almacenado en él. Sin embargo, si cambia al nuevo método de especificar miembros const estáticos, como lo hizo anteriormente,
Foo::MEMBER
ya no es un objeto. Es una constante, algo similar a: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 obtenerlo.
fuente
(int)
reparto) se produce en la unidad de traducción con una visibilidad perfecta de la constante, yFoo::MEMBER
ya no se utiliza odr . Esto está en contraste con la primera llamada a la función, donde se pasa una referencia y se evalúa en otro lugar.void push_back( const T& value );
?const&
Se pueden unir con valores.El estándar C ++ requiere una definición para su miembro const estático si la definición es de alguna manera necesaria.
Se requiere la definición, 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 usted debe definirla en el espacio de nombres.Cuando expulsa 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 realmente 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
unary +
es un valor r y, por lo tanto, se aplican las reglas para vincular los valores r con las referencias constantes y no utilizamos la dirección de nuestro miembro constante estático:fuente
push_back
era aconst &
. El uso del miembro resultó directamente en que el miembro estuviera vinculado a la referencia, lo que requería que tuviera una dirección. Sin embargo, agregar el+
crea un temporal con el valor del miembro. La referencia luego se vincula a ese temporal en lugar de requerir que el miembro tenga una dirección.Aaa.h
Aaa.cpp
fuente
No tengo idea de por qué funciona el elenco, pero Foo :: MEMBER no se asigna hasta la primera vez que se carga Foo, y como nunca lo estás cargando, nunca se asigna. Si tuviera una referencia a un Foo en alguna parte, probablemente funcionaría.
fuente
Con C ++ 11, lo anterior sería posible para tipos básicos como
La
constexpr
parte crea una expresión estática en lugar de una variable estática , y eso se comporta como una definición de método en línea extremadamente simple. Sin embargo, el enfoque resultó un poco tambaleante con los constexprs de C-string dentro de las clases de plantilla.fuente
Con respecto a la segunda pregunta: push_ref toma la referencia como parámetro, y no puede tener una referencia al miembro de const estático de una clase / estructura. Una vez que llame a static_cast, se crea una variable temporal. Y se puede pasar una referencia a este objeto, todo funciona bien.
O al menos mi colega que resolvió esto lo dijo.
fuente