Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
229 views
in Technique[技术] by (71.8m points)

c++ - If std::max() returns by reference (as it must), might that lead to a dangling reference?

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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)).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...