所以我的问题是:一个人应该使用&&不仅是编译器,还有一般代码(例如为其他开发人员创建的API成员)?
解决方法
比较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),因此对于小字符串,移动并不比副本便宜.
优化l值
pass-by-value的参数通常是你可以优化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)来强制执行移动.除了使某人不小心制作副本的可能性降低之外,它还记录了我们对数据的所有权.您不一定需要移动整个对象,您可能只是窃取对象的一部分.