通过`&&`传递的参数对非构造函数有用吗?

时间:2015-05-19 02:48:34

标签: c++ c++11 reference move move-semantics

一个人可能有一个函数void setData(std::string arg);,并通过setData(std::move(data));调用它,从而调用move构造函数,他会为void setData(std::string && arg);做同样的事情(除非他被迫将数据移入其中) )。如果对简单情况应该使用move,那么编译器会决定做什么呢?

所以我的问题是:不仅要将&&用于编译器,还要用于通用代码(例如为其他开发人员创建的API成员)?

2 个答案:

答案 0 :(得分:4)

  

如果对简单情况应该使用move,那么编译器会决定做什么呢?

即使仅使用void setData(std::string arg);,编译器也会自动移动临时值等值:

x.setData(my_string + "more");  // move from temporary std::string
x.setData(get_a_string()); // move returned-by-value string

至于具有&& - 重载的效用,请考虑一些调用代码:

std::string q = get_a_string();
myObj.setData(std::move(q));
q = pick_a_string_to_copy[rand() % 10];

此处,如果只有setData(std::string s)重载,那么s的移动构造函数将获得q的所有权,任何有效的实现都会离开{{1}没有任何指向动态分配的免费商店的指针。然后,q可能会分配给数据成员,该成员将与setData()的免费商店缓冲区交换,而ss分配为delete[]作为setData }返回。上面的下一个q =行需要为其下一个值分配一个新缓冲区(假设size()大于任何短字符串优化缓冲区)。

这与“setData(std::string&& s)超载”的情况形成对比,最终q的缓冲区可能会与myobj交换。q&#39}数据成员的缓冲区,这样&&仍拥有动态内存,可能足以存储它所分配的下一个值 - 节省一点时间。另一方面,缓冲区可能比需要的大,占用内存的时间超过了所需的时间。

简单地说,使用data.table,调用者可以交易免费存储缓冲区而不是丢失缓冲区或进行低效复制。

总而言之,功能差异通常并不相关,但确实存在。

答案 1 :(得分:4)

优化r值

比较void setData(std::string arg)void setData(std::string&& arg)。在第一种情况下,我假设setData将数据移动到位

class Widget {
  std::string data;
 public:
  void setData(std::string data) { this->data = std::move(data); }
};

如果我们这样称呼它

w.setData(std::move(data));

我们将调用move构造函数来构造函数参数,并调用move赋值运算符将数据移动到成员变量中。所以总共有两个动作。

但是如果我们为这样的r值引用重载:

class Widget {
  std::string data;
 public:
  void setData(std::string&& data) { this->data = std::move(data); }
};

您只能调用一次移动赋值运算符。你可能会说"但是动作很便宜!"这可能是真的。在某些情况下,它们并不便宜(例如std::array),而在std::string的情况下,大多数编译器都实现了小字符串优化(SSO),因此对于小字符串,移动并不比副本便宜。 / p>

优化l值

值传递的参数通常是您可以针对l值和r值进行优化,而不必提供两个重载void setData(const std::string& arg)void setData(std::string&& arg)。因此,让我们比较一下如果我们将l值传递给void setData(std::string arg) vs void setData(const std::string& arg)会发生什么。在第一种情况下,您将获得一个无条件副本,然后一个移动分配。在第二种情况下,您只需获得一项任务。额外的移动分配可能是微不足道的,但无条件副本可能比分配要昂贵得多。如果您在同一对象上多次调用setData,则分配可能能够重新使用现有容量并避免重新分配。无条件副本将始终必须进行分配。

这些考虑可能在实践中微不足道,但值得了解它们。

记录/实施所有权转移

r值参考参数的另一个用途是记录/强制执行所有权转移。假设您有一些可复制且可移动的大对象,那么您可能只提供void setData(BigData&& data)来强制执行移动。除了使某人不小心制作副本的可能性降低之外,它还记录了我们对数据的所有权。您也不一定需要移动整个对象,您可能只是窃取了对象的一部分。