template - Variable miembro de C++ 11 de tipo de referencia, comportamiento diferente después del vector push_back
vector t c++ (2)
Estaba usando la clase de otra persona que estaba actuando de manera extraña cuando lo empujé en un vector. Implica una variable miembro que es una referencia a otra variable miembro. Aquí está el ejemplo autocontenido más pequeño:
#include <iostream>
#include <vector>
class Myclass {
public:
Myclass() : a(1.0) {}
float a;
float &a_ref = a;
void addOne() {
a = a + 1.0;
}
};
int main() {
Myclass instance1;
instance1.addOne();
//prints 2:
std::cout << "instance.a_ref is " << instance1.a_ref << std::endl;
std::vector<Myclass> vec;
Myclass instance2;
vec.push_back(instance2);
vec.at(0).addOne();
//prints 1;
std::cout << "vec.at(0).a_ref is " << vec.at(0).a_ref << std::endl;
return 0;
}
Estaba compilando con g++
y -std=c++11
, así que no noté el problema por un tiempo. Ahora veo que el problema probablemente tiene que ver con el constructor de copia sintetizada y el miembro de referencia. Pero de lo que no estoy seguro es de:
- ¿Por qué hay un comportamiento diferente cuando el objeto está en un vector?
- ¿Por qué
g++
no da ninguna advertencia sobre esto, usando el estándar c ++ 11?
Pregunta extra porque tengo curiosidad:
- ¿Qué se inicializa primero,
a
oa_ref
?
El constructor de copiar / mover implícitamente definido:
[...] realiza una copia / movimiento de sus bases y miembros. [ Nota: se ignoran los inicializadores brace-o-igual de miembros de datos no estáticos. [...]
En particular, los miembros de referencia se inicializan directamente para referirse al mismo objeto al que se refiere el miembro de referencia correspondiente en el objeto fuente.
Entonces, en su caso, vec.at(0).a_ref
refiere al miembro a
de instance2
.
El compilador no lo detecta porque, en general, se espera que los miembros se refieran a un objeto de mayor duración fuera de la clase.
El problema es de hecho con el constructor de copia predeterminado. El constructor de copia predeterminado inicializa todos los miembros de los miembros del objeto fuente. Es decir, el constructor de copia predeterminado es idéntico a esto:
Myclass(const Myclass &src) :
a(src.a),
a_ref(src.a_ref)
{}
El constructor de copia predeterminado inicializa todos los miembros, por lo que ignora los inicializadores en clase.
Esta es también la razón por la que empujar en un vector causa el problema. vec.at(0)
se creó como una copia de instance2
, lo que significa que vec.at(0).a_ref
refiere a instance2.a
. Podrías verificar esto fácilmente imprimiendo sus direcciones ( ejemplo en vivo ).