函数内部传递的rvalue作为参数时会被视为lvalue吗?

5

我有一个View和一个Shape类,其中View“拥有”它的Shape对象。我将其实现为unique_ptr的向量。在函数View::add_shape(std::unique_ptr&& shape)中,我仍然需要对rvalue参数使用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));
}
2个回答

9
是的,来自参数的rvalue引用仅从调用者的角度用于选择函数,它在函数内部的行为类似于lvalue引用。
原因是您只能移动值一次,并且保持参数的rvalue-ness自动过于危险。参数类型表明此函数接受rvalue并通过提供实际移动值的实现来潜在地使用它。通过重载此版本,可以优先使用该方法而不是非移动版本。或者仅表示该函数需要一个rvalue。
在函数实现中发生的情况是另一回事。考虑一个像这样的方法:
void add_shape_twice(unique_ptr<Shape>&& shape)
{ 
  m_shapes.push_back(shape);
  m_shapes.push_back(shape);
}

如果shape参数保持为右值引用:你会意外移动值两次。在现实世界中,函数可能会更长,并且多次引用参数是非常普遍的(无论是显式地还是在循环或打包展开中),潜在错误的可能性将是巨大的。
即使我们都知道这一点并且永远不会忘记它,这也意味着我们需要cast away rvalue-ness,这会使代码非常笨拙。我们会不断添加和删除rvalue-ness。

谢谢。这回答了我的问题。虽然我认为我会使用MM的建议来传递值。 - user1057165
它在函数内部的行为类似于一个const左值引用,难道它不应该像非const左值一样行为,以便可以移动它吗? - neuront
@neuront 是的,谢谢你指出来。现在已经修复了,还有一些错别字也被修改了。 :) - Daniel Frey

4

是的,在进入具有右值引用名称的函数后,它被视为左值。因此,您必须move它。

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

或者通过值传递:

void add_shape(unique_ptr<Shape> shape)

我尝试了您传递值的建议,您是正确的。只要调用者仍然在unique_ptr上使用std::move(因为按值传递涉及复制,而unique_ptr没有复制构造函数),它就可以工作。现在一切都有意义了-通过强制调用者在unique ptr上使用std::move,清楚地表明所有权正在转移。 - user1057165
作为后续,这是否意味着通过引用传递 unique_ptr 是一个坏主意?因为这不需要调用方使用显式的 std::move,但函数仍然可以移动指针。 - user1057165
不,这并不是一个坏主意,但传值只是更短并且更少出错的做法。 - masoud

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接