阅读迈耶斯的新书我发现了一些非常相似的东西:
// compile with
// g++-4.8 --std=c++11 -Wall main3.cc && ./a.out
#include <iostream>
#include <vector>
class Widget
{
public:
using DType = std::vector<int>;
DType& data() & // lvalue
{
std::cout << "data (lvalue) : " << &data_[0] << std::endl;
return data_;
};
DType data() && // rvalue
{
std::cout << "data (rvalue) : " << &data_[0] << std::endl;
return std::move(data_);
};
// Please Note
// int parameter is here to make the overloading possible
// in a single class
DType&& data(int) &&
{
std::cout << "data (rvalue ref): " << &data_[0] << std::endl;
return std::move(data_);
};
private:
DType data_ { 0 };
};
Widget getWidget() { return Widget(); }
int main(int argc, char *argv[])
{
Widget w1;
std::vector<int> d1 = w1.data();
std::cout << "d1 copied : " << &d1[0] << std::endl;
std::vector<int> d2 = getWidget().data();
std::cout << "d2 moved : " << &d2[0] << std::endl;
std::vector<int> d3 = getWidget().data(0);
std::cout << "d3 moved : " << &d3[0] << std::endl;
return 0;
}
我的观点非常简单: 在我的盒子上,我希望我有这些结果
data (lvalue) : 0x8e28008
d1 copied : 0x8e28018
data (rvalue) : 0x8e28028
d2 moved : 0x8e28028
data (rvalue ref): 0x8e28038
d3 moved : 0x8e28038
因此,第一个向量被复制,而第二个和第三个被移动。
您可以使用两种不同的签名来实现移动操作:
一个人返回右值
DType data() && // rvalue
和一个退回右值参考
DType&& data() &&
他们取得了同样的结果:我看不到任何差异吗?什么是“最好的”?
答案 0 :(得分:2)
您可以使用两种不同的签名来实现移动操作:
这是错误的。
第一个签名,即返回DType
的签名,执行返回值的移动。第二个签名,即返回DType&&
的签名只返回一个引用。它没有移动任何东西。
移动发生在其他代码中,特别是std::vector<int> d3 =
的部分。从xvalue初始化矢量会执行移动。这就是行动,而不是功能。 然而,其他类型的操作不会执行移动:
// no move, just binding the member to a reference
std::vector<int>&& d3 = getWidget().data(0);
然而,使用第一个函数,移动始终发生:
// move into a temporary, and bind *that* to a reference
std::vector<int>&& d2 = getWidget().data();
第二个签名很危险。很容易意外地将引用返回到临时。很容易写出误导性的客户端代码,你认为某些东西已被移出但没有。返回右值引用有一个明智的用例,并且标准库已经以std::move
和std::forward
的形式处理了该用例。