If std::max() returns by reference (as it must), might that lead to a dangling reference?
c++, templates
Solution
Yes, it's a dangling reference.
Yes, it's a gotcha.
A version that returns by value might be useful provided that you use it by default, and switch to by-reference in the cases where you need it. If you use the by-reference one by default and only switch to the by-value when you need it then you'll still fall foul of the gotcha, because realizing that you need it is the same thing as realizing you should have written `const A a_= ::max(A(), A());`.
Unfortunately the by-value one introduces a new gotcha:
A a, b, c;
mymax(a, b) = c; // assigns to temporary, not to a or b
(Actually, looking at this code I reckon if you call it `max_by_val` then you won't write this).
You could return by const value, and some people used to recommend that operator overloads should return by const value. But that introduces a performance gotcha in C++11:
A a, b, c;
c = constmymax(a, b); // copies from const rvalue instead of moving.
and even in C++03 it prevents the equivalent explicit optimization `swap(c, constmymax(a,b))`.
Problem
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?