Consider the paradigmatic max template function, std::max():
// From the STL
// TEMPLATE FUNCTION _Debug_lt
template<class _Ty1, class _Ty2> inline
bool _Debug_lt(const _Ty1& _Left, const _Ty2& _Right,
_Dbfile_t _File, _Dbline_t _Line)
{ // test if _Left < _Right and operator< is strict weak ordering
if (!(_Left < _Right))
return (false);
else if (_Right < _Left)
_DEBUG_ERROR2("invalid operator<", _File, _Line);
return (true);
}
// intermediate #defines/templates skipped
// TEMPLATE FUNCTION max
template<class _Ty> inline
const _Ty& (max)(const _Ty& _Left, const _Ty& _Right)
{ // return larger of _Left and _Right
return (_DEBUG_LT(_Left, _Right) ? _Right : _Left);
}
... Or, in simpler form:
template<typename T> inline
T const & max(T const & lhs, T const & rhs)
{
return lhs < rhs ? rhs : lhs;
}
I understand why the max template must return by reference (to avoid expensive copies and to avoid the requirement for a copy constructor).
However, doesn't this lead to the possibility of dangling references, as in the following code?
int main()
{
const int & max_ = ::max(3, 4);
int m = max_; // Is max_ a dangling reference?
}
In this case, the code builds and runs fine (VS 2010) and the value m is set to 4. However, it strikes me that max_ is a dangling reference since the hard-coded rvalues 3 and 4 were passed directly to max, and the storage allocated for these hard-coded rvalue constants can rightfully be de-allocated by the time the following line of code is reached.
I could envision analogous edge-cases for full-fledged objects, such as
class A
{
friend bool operator<(A const &, A const &)
{
return false;
}
};
int main()
{
const A & a_ = ::max(A(), A());
A a = a_; // Is a_ a dangling reference?
}
Am I correct that this usage of max - in which rvalues defined within the call argument list are passed as arguments - is an example of a potential "gotcha" with the use of max, unavoidable since max needs to be defined to return a reference in order to avoid expensive copies (and to avoid the requirement of a copy constructor)?
Might there be real-life circumstances in which it would be good programming practice to define a version of max that returns its result by value, rather than by reference, for the reason discussed in this question?