【问题标题】:is rvalue passed as parameter treated as lvalue inside the function?右值是否作为参数传递,在函数内被视为左值?
【发布时间】:2013-09-28 03:18:28
【问题描述】:

我有一个 View 和一个 Shape 类,其中 View “拥有”它的 Shape 对象。 我将其实现为 unique_ptr 的向量。 在函数 View::add_shape(std::unique_ptr&& shape) 中,我仍然需要在右值参数上使用 std::move 来使其编译。为什么? (使用 GCC 4.8)

#include <memory>
#include <vector>
using namespace std;

class Shape { };
class View
{
  vector<unique_ptr<Shape>> m_shapes;
  public:
  void add_shape(unique_ptr<Shape>&& shape)
  { 
    m_shapes.push_back(std::move(shape));// won't compile without the std::move
  }
};


int main()
{
  unique_ptr<Shape> ups(new Shape);
  View v;
  v.add_shape(std::move(ups));
}

【问题讨论】:

    标签: c++ c++11 rvalue


    【解决方案1】:

    是的,参数的右值引用仅用于从调用者的角度选择函数,它的行为就像函数内部的左值引用。

    原因是您只能移动一次值,并且自动保持参数的右值性被认为太危险了。参数类型表明此函数接受一个右值,并通过提供一个实际移动该值的实现来潜在地使用它。通过重载这个版本的方法,可以使用有利于非移动版本的方法。或者它只是说函数需要一个右值。

    函数实现中发生的事情是另一回事。考虑一个类似的方法

    void add_shape_twice(unique_ptr<Shape>&& shape)
    { 
      m_shapes.push_back(shape);
      m_shapes.push_back(shape);
    }
    

    如果shape 作为参数仍然是右值引用:您可能会不小心将值移动了两次。由于在现实世界中函数可能更长,并且多次引用参数是很常见的(显式地或在循环或包扩展中),因此出错的可能性将是巨大的。

    即使我们都知道它并且永远不会忘记它,这也意味着我们需要抛弃右值性,这会使代码变得非常笨拙。我们会不断地添加和删除右值性。

    【讨论】:

    • 谢谢。这就回答了这个问题。虽然我觉得我会用MM的建议传值
    • “它的行为就像函数内部的 const 左值引用”难道不应该像 non-const 左值那样可以被移动吗?
    • @neuront 是的,感谢您指出。它现在已经修复,还有一些错别字。 :)
    【解决方案2】:

    是的,在进入函数后,右值引用现在有了名称,所以它被当作左值对待。因此你必须move它。

    void add_shape(unique_ptr<Shape>&& shape)
                                       ^^^^^
    

    或者按值传递:

    void add_shape(unique_ptr<Shape> shape)
    

    【讨论】:

    • 我试过你的按值传递的建议,你是对的。只要调用者仍然在 unique_ptr 上使用 std::move 它就可以工作(因为按值传递涉及副本而 unique_ptr 没有副本 ctr)。现在这一切都说得通了——通过强制调用者在唯一的 ptr 上使用 std::move,很明显所有权正在转移。
    • 作为后续,这是否意味着通过引用传递 unique_ptr 是一个坏主意?因为这不需要调用者使用显式 std::move ,但函数仍然可以移动指针。
    • 不,这不是坏主意,但是按值传递更短,更不容易出错。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-13
    • 2020-03-08
    • 2015-04-13
    • 1970-01-01
    • 2013-02-21
    • 2016-04-11
    • 2020-11-27
    相关资源
    最近更新 更多