c++ - siguientes - Referencia de enlace a un objeto antes de la construcción
ruta predeterminada cisco (2)
[...] no hay mención de referencias en el ejemplo [...]
Quieres decir, excepto por
[...] refiriéndose a cualquier miembro no estático [...]
Del pasaje que citas, diría que esta línea provoca un comportamiento indefinido:
int& r3 = yobj.i; // #3
porque tú eres:
[...] refiriéndose a cualquier miembro no estático o clase base del objeto antes de que el constructor comience la ejecución [.]
Además, para esto:
y lo más importante, el párrafo anterior no implica que, de lo contrario, el comportamiento esté bien definido.
Tienes razón, no es así:
Se puede esperar un comportamiento indefinido cuando esta Norma Internacional omite cualquier definición explícita de comportamiento o cuando un programa utiliza una construcción errónea o datos erróneos.
¿Está bien definido el comportamiento del siguiente código?
struct X { int i; }; // trivial
struct Y : X { Y(){} }; // non-trivial
extern X xobj;
int& r1 = xobj.i; // #1
X xobj;
extern Y yobj;
Y& r2 = yobj; // #2
// int& r3 = yobj.i; // #3 - this is UB according to the standard
Y yobj;
Este código está inspirado en el ejemplo del estándar C ++, a saber, el borrador N4140 [class.cdtor] / 1.
Eso es lo que dice el párrafo:
Para un objeto con un constructor no trivial, hacer referencia a cualquier miembro no estático o clase base del objeto antes de que el constructor comience la ejecución da como resultado un comportamiento indefinido. Para un objeto con un destructor no trivial, hacer referencia a cualquier miembro no estático o clase base del objeto después de que el destructor finalice la ejecución da como resultado un comportamiento indefinido.
A continuación, se muestra un ejemplo que muestra cómo los punteros pueden y no estar vinculados a los objetos.
Intuitivamente, parece que #1
y #2
están bien definidos, mientras que #3
invoca a UB si no está comentado, pero, primero, los ejemplos no son normativos, segundo, no hay mención de referencias en el ejemplo, y tercero y el más importante, el párrafo anterior no implica que de lo contrario el comportamiento está bien definido. O lo hace? ¿O tal vez hay otra cita relevante en el estándar que me perdí?
Editar : La respuesta puede (posiblemente) ser sí si los objetos tienen una duración de almacenamiento estática, pero también pueden ser locales, por ejemplo:
struct A { A(){} };
struct B { B(A&){} };
struct C {
B b;
A a;
C() : b(a) {}
};
int main() {
C c;
}
En realidad, esta fue la inspiración inicial para esta pregunta, consulte Dependencia circular en la lista de inicialización del constructor
El comportamiento de # 2 definitivamente está bien definido. Como lo menciona @dyp, el párrafo relevante está en [basic.life]:
El enlace del glvalue yobj
a una referencia está bien, ya que su almacenamiento dura toda la duración del programa ([basic.stc.static] / 1) y la referencia está vinculada a un objeto válido - aliveness a un lado - que cumple con el requisito en ( [dcl.ref] / 5). De forma similar, para el segundo ejemplo que mostró, siempre que no se realice ninguna operación en miembros del subobjeto A
, el párrafo anterior también se aplica puesto que se llama al constructor de C
en el almacenamiento asignado al que se refiere.