一个人可能有一个函数void setData(std::string arg);
,并通过setData(std::move(data));
调用它,从而调用move构造函数,他会为void setData(std::string && arg);
做同样的事情(除非他被迫将数据移入其中) )。如果对简单情况应该使用move,那么编译器会决定做什么呢?
所以我的问题是:不仅要将&&
用于编译器,还要用于通用代码(例如为其他开发人员创建的API成员)?
答案 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()
的免费商店缓冲区交换,而s
将s
分配为delete[]
作为setData
}返回。上面的下一个q =
行需要为其下一个值分配一个新缓冲区(假设size()
大于任何短字符串优化缓冲区)。
这与“setData(std::string&& s)
超载”的情况形成对比,最终q
的缓冲区可能会与myobj
交换。q
&#39}数据成员的缓冲区,这样&&
仍拥有动态内存,可能足以存储它所分配的下一个值 - 节省一点时间。另一方面,缓冲区可能比需要的大,占用内存的时间超过了所需的时间。
简单地说,使用data.table
,调用者可以交易免费存储缓冲区而不是丢失缓冲区或进行低效复制。
总而言之,功能差异通常并不相关,但确实存在。
答案 1 :(得分:4)
比较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值和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)
来强制执行移动。除了使某人不小心制作副本的可能性降低之外,它还记录了我们对数据的所有权。您也不一定需要移动整个对象,您可能只是窃取了对象的一部分。