The range initialization line of a for(:)
loop does not extend lifetime of anything but the final temporary (if any). Any other temporaries are discarded prior to the for(:)
loop executing.
Now, do not despair; there is an easy fix to this problem. But first a walk through of what is going wrong.
The code for(auto x:exp){ /* code */ }
expands to, basically:
{
auto&& __range=exp;
auto __it=std::begin(__range);
auto __end=std::end(__range);
for(; __it!=__end;++__it){
auto x=*__it;
/* code */
}
}
(With a modest lies on the __it
and __end
lines, and all variables starting with __
have no visible name. Also I am showing C++17 version, because I believe in a better world, and the differences do not matter here.)
Your exp
creates a temporary object, then returns a reference to within it. The temporary dies after that line, so you have a dangling reference in the rest of the code.
Fixing it is relatively easy. To fix it:
std::string const& func() const& // notice &
{
return m.find("key")->second;
}
std::string func() && // notice &&
{
return std::move(m.find("key")->second);
}
do rvalue overloads and return moved-into values by value when consuming temporaries instead of returning references into them.
Then the
auto&& __range=exp;
line does reference lifetime extension on the by-value returned string
, and no more dangling references.
As a general rule, never return a range by reference to a parameter that could be an rvalue.
Appendix: Wait, &&
and const&
after methods? rvalue references to *this
?
C++11 added rvalue references. But the this
or self parameter to functions is special. To select which overload of a method based on the rvalue/lvalue-ness of the object being invoked, you can use &
or &&
after the end of the method.
This works much like the type of a parameter to a function. &&
after the method states that the method should be called only on non-const rvalues; const&
means it should be called for constant lvalues. Things that don't exactly match follow the usual precidence rules.
When you have a method that returns a reference into an object, make sure you catch temporaries with a &&
overload and either don't return a reference in those cases (return a value), or =delete
the method.