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?
See Question&Answers more detail:
os