The following code looks rather harmless on first sight. A user uses the function bar()
to interact with some library functionality. (This may have even worked for a long time since bar()
returned a reference to a non-temporary value or similar.) Now however it is simply returning a new instance of B
. B
again has a function a()
that returns a reference to an object of the iterateable type A
. The user wants to query this object which leads to a segfault since the temporary B
object returned by bar()
is destroyed before the iteration begins.
I am indecisive who (library or user) is to blame for this. All library provided classes look clean to me and certainly aren't doing anything different (returning references to members, returning stack instances, ...) than so much other code out there does. The user doesn't seem to do anything wrong as well, he's just iterating over some object without doing anything concerning that objects lifetime.
(A related question might be: Should one establish the general rule that code shouldn't "range-based-for-iterate" over something that is retrieved by more than one chained calls in the loop header since any of these calls might return a 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;
}
}