O código a seguir parece bastante inofensivo à primeira vista. Um usuário usa a função bar()
para interagir com algumas funcionalidades da biblioteca. (Isso pode até ter funcionado por um longo tempo desde que bar()
retornou uma referência a um valor não temporário ou similar.) Agora, no entanto, está simplesmente retornando uma nova instância de B
. B
novamente tem uma função a()
que retorna uma referência a um objeto do tipo iterateable A
. O usuário deseja consultar este objeto, o que leva a um segfault, pois o B
objeto temporário retornado por bar()
é destruído antes do início da iteração.
Eu sou indeciso quem (biblioteca ou usuário) é o culpado por isso. Todas as classes fornecidas pela biblioteca parecem limpas para mim e certamente não estão fazendo nada diferente (retornando referências a membros, retornando instâncias de pilha, ...) do que muitos outros códigos existentes. O usuário parece não fazer nada de errado também, ele está apenas repetindo algum objeto sem fazer nada a respeito da vida útil do objeto.
(Uma pergunta relacionada pode ser: Se alguém estabelecer a regra geral de que o código não deve "basear-se na iteração" sobre algo recuperado por mais de uma chamada em cadeia no cabeçalho do loop, pois qualquer uma dessas chamadas pode retornar um 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;
}
}