Следующий код выглядит довольно безобидным на первый взгляд. Пользователь использует функцию bar()
для взаимодействия с некоторыми функциями библиотеки. (Это могло работать даже долгое время, так как bar()
возвращало ссылку на временное значение или подобное.) Однако теперь оно просто возвращает новый экземпляр B
. B
снова имеет функцию, a()
которая возвращает ссылку на объект повторяемого типа A
. Пользователь хочет запросить этот объект, что приводит к segfault, поскольку B
возвращаемый временный объект bar()
уничтожается до начала итерации.
Я не знаю, кто (библиотека или пользователь) виноват в этом. Все предоставленные библиотекой классы кажутся мне понятными и, конечно, не делают ничего другого (возвращая ссылки на члены, возвращая экземпляры стека, ...), чем многие другие программы. Пользователь, похоже, тоже не делает ничего плохого, он просто перебирает какой-то объект, не делая ничего относительно времени жизни этого объекта.
(С этим может быть связан вопрос: следует ли установить общее правило, согласно которому код не должен «основываться на диапазоне для итерации» над чем-то, что извлекается более чем одним соединением вызовов в заголовке цикла, поскольку любой из этих вызовов может вернуть 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;
}
}