El siguiente código parece bastante inofensivo a primera vista. Un usuario usa la función bar()
para interactuar con alguna funcionalidad de biblioteca. (Esto puede haber funcionado durante mucho tiempo desde que bar()
devolvió una referencia a un valor no temporal o similar). Ahora, sin embargo, simplemente está devolviendo una nueva instancia de B
. B
nuevamente tiene una función a()
que devuelve una referencia a un objeto del tipo iterable A
. El usuario desea consultar este objeto, lo que conduce a una falla predeterminada, ya que el B
objeto temporal devuelto por bar()
se destruye antes de que comience la iteración.
No estoy decidido a quién (biblioteca o usuario) tiene la culpa de esto. Todas las clases proporcionadas por la biblioteca me parecen limpias y ciertamente no están haciendo nada diferente (devolviendo referencias a miembros, devolviendo instancias de pila, ...) que tanto otro código por ahí hace. El usuario no parece hacer nada mal también, solo está iterando sobre algún objeto sin hacer nada relacionado con la vida útil de ese objeto.
(Una pregunta relacionada podría ser: ¿Debería establecerse la regla general de que el código no debe "basarse en el rango para iterar" sobre algo que es recuperado por más de una llamada encadenada en el encabezado del bucle ya que cualquiera de estas llamadas puede devolver un rvalue?)
#include <algorithm>
#include <iostream>
// "Library code"
struct A
{
A():
v{0,1,2}
{
std::cout << "A()" << std::endl;
}
~A()
{
std::cout << "~A()" << std::endl;
}
int * begin()
{
return &v[0];
}
int * end()
{
return &v[3];
}
int v[3];
};
struct B
{
A m_a;
A & a()
{
return m_a;
}
};
B bar()
{
return B();
}
// User code
int main()
{
for( auto i : bar().a() )
{
std::cout << i << std::endl;
}
}