When you write a function thus...
const S& f(int i) const { std::cout << i << "\n"; return *this; }
...you're instructing the compiler to return a const S&
and you are taking responsibility for ensuring the referenced object has a lifetime suitable for the caller's use. ("ensuring" may constitute documenting client usage that works properly with your design.)
Often - with typical separation of code into headers and implementation files - f(int) const
's implementation won't even be visible to calling code, and in such cases the compiler has no insight regarding to which S
a reference might be returned, nor whether that S
is a temporary or not, so it has no basis on which to decide whether the lifetime needs to be extended.
As well as the obvious options (e.g. trusting clients to write safe code, returning by value or smart pointer), it's worth knowing about a more obscure option...
const S& f(int i) const & { ...; return *this; }
const S f(int i) const && { ...; return *this; }
The &
and &&
immediately before the function bodies overload f
such that the &&
version is used if *this
is movable, otherwise the &
version is used. That way, someone binding a const &
to f(...)
called on an expiring object will bind to a new copy of the object and have the lifetime extended per the local const
reference, while when the object isn't expiring (yet) the const
reference will be to the original object (which still isn't guaranteed live as long as the reference - some caution needed).