【问题标题】:Const reference to temporary object does not extend its lifetime对临时对象的 const 引用不会延长其生命周期
【发布时间】:2019-11-04 16:23:28
【问题描述】:

我有一个从中创建临时对象的类。我可以将 const 引用绑定到这个临时对象,它按预期工作。但是,如果我在这个临时对象上调用一个返回 std::move(*this) 的成员函数,并绑定到这个返回值,它就不会像我预期的那样工作。下面的短代码重现了我的问题并且不言自明。

#include <iostream>
#include <cstdlib>
#include <vector>

class myval {
    public:
    std::vector<int> i;
    myval(int i) : i({i}) {}
    myval(const myval& v) = delete;
    myval(myval&& v) : i(std::move(v.i)) {}
    myval&& addone() && {
        i[0]++;
        return std::move(*this);
    }

};

int main()
{
    //Object is moved, works like expected
    const auto moved_value = myval{7}.addone();
    std::cout << moved_value.i[0] << std::endl;

    //Const reference is supposed extend the lifetime of a temporary object
    const auto& u = myval{7};
    //Prints 7, as expected
    std::cout << u.i[0] << std::endl;

    const auto& v = myval{7}.addone();
    //Why does this print 0?
    std::cout << v.i[0] << std::endl;

    return 0;
}

编辑:

  1. 考虑到对此的解释,是否有可能完成这项工作以使分配“const auto&amp; v = ....”有效?

  2. 为什么下面的作业有效而我的无效?

    const auto& s = std::string("hi")[1];
    std::cout << s;
    

【问题讨论】:

    标签: c++ c++14 c++17


    【解决方案1】:

    const 引用只会延长函数本地临时对象的生命周期。在行中

    const auto& v = myval{7}.addone();
    

    您的参考不绑定到临时。 addone 通过引用返回,这意味着您正在使用左值而不是临时值,因此当完整表达式结束时,您将获得对不再存在的对象的引用。


    关于编辑。制作

    const auto& v = myval{7}.addone();
    

    我会将addone 更改为按值返回

    myval addone() && {
        i[0]++;
        return std::move(*this);
    }
    

    这将为您提供正确的行为,因为它将对象移动到一个临时对象中,然后您可以延长生命周期。

    对于

    const auto& s = std::string("hi")[1];
    std::cout << s;
    

    你的代码有未定义的行为,不幸的是你得到了你所期望的。您正在执行与上一个示例相同的操作,并且您创建的临时字符串没有生命周期延长。

    【讨论】:

    • 我不明白为什么 addone() 通过引用返回。你能详细说明一下吗?
    • @OblivionreinstateOurMonica 签名是 myval&amp;&amp; addone() &amp;&amp;myval&amp;&amp; 部分表示它返回一个右值引用。
    • 所以你说在这种情况下引用和右值引用是一回事?
    • @OblivionreinstateOurMonica 是的。右值和左值引用都是引用。正是这种引用性使它们成为左值。这就是为什么你必须在右值引用上使用std::move 才能移动它们引用的东西。
    • @DDaniel 您的代码无法“工作”。您有未定义的行为,不幸的是,您期望的结果是可能结果集的一部分。
    【解决方案2】:

    引用生命周期延长仅在直接应用于前额临时对象时才有效。

    应用于引用时没有引用生命周期延长。

    myval&& addone() && {
        i[0]++;
        return std::move(*this);
    }
    

    返回一个引用。 (右值和左值引用都是引用)

    const auto& v = myval{7}.addone();
    

    这不会延长引用的生命周期,因为我们将 const auto&amp; v 绑定到引用,而不是临时的。


    我们怎样才能让它发挥作用?好吧,重写addone的签名我们得到:

    myval addone() && {
        i[0]++;
        return std::move(*this);
    }
    

    现在它返回一个纯右值而不是一个引用。

    const auto& v = myval{7}.addone();
    

    现在这个引用生命周期延长了。

    您的“为什么会这样”跟进是因为未定义的行为可以做任何事情,包括“似乎可以工作”。这不是有效的代码。

    【讨论】:

    • 在你的例子中,它返回一个prvalue而不是一个引用,它仍然应该说return std::move(*this)return *this;吗?会有什么不同?
    • @DDaniel 如果您 return *this 您通过复制返回:*this 即使在右值限定方法中也是左值。如果你 return std::move(*this) 你通过移动构造返回。 prvalue c++14c++17 之间的变化超出了这个问题的范围,所以我的措辞必须小心。
    猜你喜欢
    • 2012-11-15
    • 2019-01-06
    • 2013-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多