19

考虑范式max模板函数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);
    }

...或者,以更简单的形式:

template<typename T> inline
T const & max(T const & lhs, T const & rhs)
{
    return lhs < rhs ? rhs : lhs;
}

我理解为什么max模板必须通过引用返回(以避免昂贵的副本并避免需要复制构造函数)。

但是,这不会导致悬空引用的可能性,如以下代码所示?

int main()
{
    const int & max_ = ::max(3, 4);
    int m = max_; // Is max_ a dangling reference?
}

在这种情况下,代码构建并运行良好(VS 2010)并且值m设置为 4。但是,我觉得这max_是一个悬空引用,因为硬编码的右值直接传递给,3并且为这些分配的存储硬编码的右值常量可以在到达以下代码行时正确地被释放。4max

我可以为成熟的对象设想类似的边缘情况,例如

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?
}

我是否正确,这种用法max- 其中在调用参数列表中定义的右值作为参数传递 - 是使用 的潜在“陷阱”的一个例子,这是max不可避免的,因为max需要定义为返回引用以避免昂贵的副本(并避免需要复制构造函数)?

出于这个问题中讨论的原因,在现实生活中是否可能存在良好的编程习惯来定义一个max按值而不是按引用返回其结果的版本?

4

2 回答 2

17

是的,这是一个悬而未决的参考。

是的,这是一个问题。

按值返回的版本可能很有用,前提是您默认使用它,并在需要时切换到按引用。如果您默认使用按引用,并且仅在需要时才切换到按值,那么您仍然会遇到问题,因为意识到您需要它与意识到您应该编写const A a_= ::max(A(), A());.

不幸的是,按价值引入了一个新问题:

A a, b, c;
mymax(a, b) = c; // assigns to temporary, not to a or b

(实际上,看看这段代码,我认为如果你调用它,max_by_val那么你就不会写这个)。

您可以按 const 值返回,并且有些人过去建议运算符重载应按 const 值返回。但这在 C++11 中引入了一个性能问题:

A a, b, c;
c = constmymax(a, b); // copies from const rvalue instead of moving.

甚至在 C++03 中,它也阻止了等效的显式优化swap(c, constmymax(a,b))

于 2012-12-05T11:12:19.270 回答
5

(已删除答案)请参阅 Herb Sutters GOTW 文章,将 const 引用绑定到临时对象。

编辑:我删除了答案,因为它实际上是错误的(感谢您让我清楚),所以我留下的只是一篇相关文章的链接。但请注意:本文中描述的功能不适用于此处!

于 2012-12-05T11:13:02.733 回答