【问题标题】:c++: const reference to an rvaluec++: 对右值的 const 引用
【发布时间】:2013-11-21 04:52:22
【问题描述】:
//old school '98 c++, no C++0x stuff
std::string getPath();

void doSomething()
{
    const std::string &path = getPath(); //const reference to an rvalue
    ... // doSomething with path
}

void doSomething2()
{
    const std::string path = getPath(); //regular variable
    ... // doSomething with path
}

doSomething 和 doSomething2 有什么区别,哪个更受欢迎?
在 doSomething 中使用 const 引用返回的右值是否安全?
doSomething2 是否创建返回的右值的副本,编译器是否允许在这里进行右值优化?

【问题讨论】:

    标签: c++


    【解决方案1】:

    在 doSomething 中使用 const 引用返回的右值是否安全?

    是的,这很好。该语言有一个特定的子句,它保证如果您将引用绑定到 rvalue,则会创建一个临时值,并且生命周期会延长到创建引用的范围结束。

    doSomething2 是否创建返回的右值的副本,编译器是否允许在这里进行右值优化?

    在这两种情况下,成本是相同的,因为编译器会做 RVO(返回值优化)

    doSomething 和 doSomething2 有什么区别,哪个更受欢迎?

    主要区别在于,在一种情况下,您为真实对象命名,而在另一种情况下,该名称被命名为参考。编译器应该在两个版本中生成完全相同的代码。

    话虽如此,我发现const& 的使用具有误导性。它给人的印象是本地函数在某处引用了某个对象,但它确实有一个副本(没有名称)。为了意识到这是一个副本,维护者必须查看并验证被调用函数的签名。在值的情况下,行为更明显:函数维护函数返回的任何内容的副本(可能是值或引用)。

    第二个区别是你只能合法地绑定一个 const 引用,这意味着函数不能修改对象。在存储值的情况下,类型可以是非常量并且函数可以修改该对象,因此这种方法更加灵活。

    在 C++03 中,使用 const& 技巧的唯一原因是你知道函数返回一个值,你知道类型派生自一个众所周知的基,并且你不想命名类型 [看看ScopeGuard]。在 C++11 中,使用不再重要,因为您可以使用 auto 让编译器自动识别类型。

    【讨论】:

    • 让事情变得更复杂...stackoverflow.com/questions/17236007/…
    • 这就是操作所提到的 - //old school c++, no C++0x stuff
    • 自 98 标准以来,这种行为一直存在于 C++ 中。如果 old school 是 98 之前的意思,那我对当时的 C++ 是什么一无所知。但在 98 及以下标准中,保证是存在的。
    • 老派我指的是普通的 c++ (98/03),而不是史前/古代 c++ (pre 98) :)
    【解决方案2】:

    (从其他 cmets 看来,当您绑定对关于其生命周期的右值的引用时,这可能是 C++ 标准版本依赖于保证者 - 这个答案反映了 C++ 经典)

    doSomething 引用了什么?

    doSomething2 有一个可能不再存在的东西的副本,这很好

    让我们看看 getPath():

    通常它会获取返回值,将其复制到堆栈中,然后将其复制到 lhs 它可以直接分配给 lhs

    它可以返回“hello”,这将在堆栈上创建一个字符串 - 这不能被引用,因为它是临时的(doSomething 的问题)

    它可以返回一个静态 const 字符串 - 在这种情况下,它可以完全内联并分配给 lhs

    【讨论】:

    • 这是错误的,语言保证临时生命周期会被延长到引用生存的范围结束。
    • @DavidRodríguez-dribeas - 我注意到您的回答并正在编辑我的回复以提及 - 标准版本可能因终身保证而异 - 此答案可能对那些仍然拥有较旧编译器并具有核心转储的人有用 -注意 OP 评论 //old school c++, no C++0x stuff
    【解决方案3】:

    如果std::string 在函数getPath() 内分配并在从该函数返回时被销毁,则引用版本可能有一个悬空引用。 然后,这是危险的用法。

    如果您有一个持有该字符串的类,并且该类超出了 const std::string & 引用的范围,例如 MyClass::getPath(),那么,在这种情况下,引用不会悬空。

    第二个版本将始终有效。

    【讨论】:

    • 这是错误的,语言保证引用可以被绑定,并且临时文件的生命周期将在引用所在的范围内延长。
    • 即使在堆栈中分配并且函数返回?我觉得很奇怪,在这种情况下你使用的是 const lavalue 引用。
    • 请注意,引用绑定到按值返回的函数。临时对象的生命周期延长到当前范围的末尾(引用所在的位置)。如果函数退出(返回或抛出),则关闭引用的范围并销毁临时对象。
    • getPath 的声明很清楚,它如何做到这一点无关紧要
    猜你喜欢
    • 2012-11-30
    • 2014-09-09
    • 2011-08-29
    • 1970-01-01
    • 2021-12-22
    • 2017-04-13
    • 2018-08-22
    相关资源
    最近更新 更多