¿Qué texto estándar nos dice que la extensión temporal de la vida de ref-to-const solo “funciona una vez”?

Me mostraron el siguiente ejemplo en el chat:

#include  struct foo { ~foo() { std::cout << "destroying!\n"; } }; const foo& func(const foo& a, const foo&) { return a; } int main() { foo x; const foo& y = func(foo(), x); std::cout << "main\n"; } 

Salida :

 destroying! main destroying! 

Parece demostrar que la vida del foo temporal no se extiende a la totalidad de main , aunque se limita a un ref-to- const en ese ámbito.

Presumiblemente, entonces, la extensión de por vida solo “funciona una vez”; es decir, se aplica cuando los argumentos de func se inicializan, pero no se transmiten a través de enlaces consecutivos.

¿Mi interpretación es correcta? Si es así (y si algún párrafo individual es directamente aplicable) ¿cuál es la redacción estándar que define este comportamiento?

Esto está sujeto a dos informes temáticos, http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1299 y http://www.open-std.org/jtc1/sc22/ wg21 / docs / cwg_active.html # 1568 .

El primer informe de problema, del cual soy periodista, tenía la intención de cubrir todos estos casos en los que una referencia está vinculada a un objeto temporal, pero no se pretende que se extienda a lo largo de la vida. La descripción en el cuerpo del problema solo menciona que los valores pr se confunden con expresiones temporales (que en realidad deciden si la duración de lo que evalúan se alarga o no). Pero lvalue y xvalues ​​también se confunden con estos en el estándar. Un ejemplo de lo que ocurre en el contexto de static_cast es el número de problema # 1568 (en el que el uso de “variable temporal” confunde aún más el asunto).

En realidad, esto:

Un límite temporal a un parámetro de referencia en una llamada a función (5.2.2) persiste hasta la finalización de la expresión completa que contiene la llamada.

Contradice las otras reglas en el mismo párrafo. Porque el temporal está vinculado tanto a un parámetro de referencia en una llamada de función como a una variable de referencia automática local.

Estás casi en lo correcto. Este comportamiento en realidad proviene de la llamada de función específicamente, no a causa de ningún tipo de regla “solo funciona una vez”.

Aquí está la redacción de toda la “característica” de extensión de por vida, con la regla pertinente resaltada en negrita:

[C++11: 12.2/5]: [..] El temporal al que está vinculada la referencia o el temporal que es el objeto completo de un subobjeto al que está vinculada la referencia persiste durante el tiempo de vida de la referencia, excepto :

  • [..]
  • Un límite temporal a un parámetro de referencia en una llamada a función (5.2.2) persiste hasta la finalización de la expresión completa que contiene la llamada.
  • [..]

La regla que se aplica aquí es de sentido común. El estándar está mal redactado, y de hecho lo garantiza. Pero no hay una forma práctica de implementarlo.

Probablemente soy un poco lento, pero para mí no quedó claro cuál es la resolución de esta pregunta al leer las otras respuestas. Por lo tanto, modifiqué el código que se muestra y quise resumirlo para los demás: la respuesta es: si accedes a y obtendrás un comportamiento indefinido .

Ejecute este código:

 struct foo { int id; foo(int id) : id(id) { std::cout << "ctor " << id << std::endl; }; ~foo() { std::cout << "dtor " << id << std::endl; } }; const foo& func(const foo& a, const foo&) { return a; } int main(int argc, char** argv) { foo x(1); const foo& y = func(foo(2), x); std::cout << "main " << y.id << std::endl; return 0; } 

La salida para mí es:

 ctor 1 ctor 2 dtor 2 main 2 dtor 1 

Pero la línea main 2 es un comportamiento indefinido .