如果std :: max()通过引用返回(必须),那么可能导致悬空引用吗?

Dan*_*aum 16 c++ templates

考虑范例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);
    }
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

在这种情况下,代码生成并运行正常(VS 2010)和值m设置为4.但是,这让我感到max_是因为硬编码的右值悬空参考3,并4直接传递给max,将它们的存储分配在到达下一行代码时,可以正确地解除分配硬编码的右值常量.

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

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?
}
Run Code Online (Sandbox Code Playgroud)

我是否正确使用max- 在调用参数列表中定义的rvalues作为参数传递 - 是一个使用的潜在"gotcha"的示例max,不可避免,因为max需要定义为返回引用以避免昂贵的副本(并避免复制构造函数的要求)?

max对于这个问题中讨论的原因,可能存在现实情况,在这种情况下,定义一个版本的返回结果的价值而不是引用将是一个好的编程实践吗?

Ste*_*sop 15

是的,这是一个晃来晃去的参考.

是的,这是一个问题.

如果您默认使用它,则按值返回的版本可能很有用,并且在您需要的情况下切换到by-reference.如果你默认使用by-reference,并且只在你需要时切换到by-value,那么你仍然会犯下这个问题,因为意识到你需要它就像意识到你应该写的一样const A a_= ::max(A(), A());.

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

A a, b, c;
mymax(a, b) = c; // assigns to temporary, not to a or b
Run Code Online (Sandbox Code Playgroud)

(实际上,看看这段代码,我认为如果你打电话给它,max_by_val那么你就不会写这个).

你可以通过const值返回,有些人曾经建议运算符重载应该返回const值.但这引入了C++ 11中的性能问题:

A a, b, c;
c = constmymax(a, b); // copies from const rvalue instead of moving.
Run Code Online (Sandbox Code Playgroud)

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

  • @Caribou:不要相信代表,因为你知道我是一个自信的傻瓜,而Arne是一个C++专家,他在网站上不是很活跃.IIRC标准中的措辞是微妙的误导,它只是说"如果参考被绑定到临时那么......".它没有说明通过另一个引用初始化引用不是为了那个目的而"绑定到临时引用". (7认同)
  • all:编辑并删除了我的错误答案.感谢您指出并给我机会学习新的东西.@SteveJessop:是的,过去并不是很活跃,事实上,自我注册帐户以来只有两周时间.但我不会称我为C++大师 - 无论如何,谢谢你的赞美:-) (2认同)

Arn*_*rtz 5

(已删除答案)请参阅Herb Sutters GOTW关于绑定const对temporaries的引用的文章.

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

  • 这段代码没有延长.临时必须直接绑定到引用.使用恰好引用临时的另一个引用初始化引用`a_`不会将临时扩展到`a_`的生命周期. (7认同)