我想实现一个填充向量然后返回右值引用的函数。我厌倦了:
std::vector<int> &&fill_list() {
std::vector<int> res;
... do something to fill res ...
return res;
}
int main(int argc, char **argv) {
std::vector<int> myvec = fill_list();
return 0;
}
但这不起作用,我收到以下错误:
error: invalid initialization of reference of type 'std::vector<int>&&' from expression of type 'std::vector<int>'
所以,总而言之,正确的做法是怎样的?我认为我还没有获得右值参考。
答案 0 :(得分:29)
你似乎对rvalue引用是什么以及它与移动语义的关系感到困惑。
首先是第一件事:&&
并不意味着移动。它只不过是一种特殊的引用类型。它仍是引用。这不是一个价值;它不是移动的值;它是一个值的引用。这意味着它具有参考类型的所有限制。值得注意的是,它必须引用仍然存在的值。因此,返回一个悬空的r值引用并不比返回一个悬空的l值引用更好。
“移动”是让一个对象声明对另一个对象的内容的所有权的过程。 R值引用促进移动语义,但简单地使用&&
并不意味着任何移动。当调用移动构造函数(或移动赋值运算符)时,会发生仅运动;除非调用这两件事中的一件,否则不会发生任何动作。
如果您希望将std::vector
的内容移出用户,只需执行以下操作:
std::vector<int> fill_list() {
std::vector<int> res;
... do something to fill res ...
return res;
}
考虑到fill_list()
的使用情况:
std::vector<int> myvec = fill_list();
将会发生以下两件事之一。返回将被省略,这意味着不会发生复制或移动。 res
直接构建到myvec
。或者res
将被移动到返回值,然后执行myvec
的移动初始化。所以再一次,没有复制。
如果你有这个:
std::vector<int> myvec;
myvec = fill_list();
然后,它将移入。没有复制。
C ++ 11知道何时隐式移动东西是安全的。通过值而不是通过引用或某种方式返回值始终是一个安全的移动时间。因此,它会移动。
答案 1 :(得分:0)
关于右值引用的讨论,你可以阅读C ++的作者Bjarne Stroustrup在这里对它们说些什么:
http://www2.research.att.com/~bs/C++0xFAQ.html#rval
解决您的具体示例,简短的回答是,由于命名返回值优化 - 这是事实上的标准C ++编译器功能,甚至在C ++之前11 - 如果您只是返回 - by-value编译器会像你想要的那样有效地占用res和myvec:
std::vector<int> fill_list() {
std::vector<int> res;
... do something to fill res ...
cout << &res << endl; // PRINT POINTER
return res;
}
int main(int argc, char **argv) {
std::vector<int> myvec = fill_list();
cout << &myvec << endl; // PRINT POINTER
return 0;
}
在两种情况下,两个“PRINT POINTER”行将打印相同指针。
上面的向量myvec和向量res将是具有相同存储的相同向量。不会调用任何复制构造函数或移动构造函数。从某种意义上说,res和myvec将是同一个对象的两个别名。
这甚至比使用移动构造函数更好。 myvec被构建并“就地填满”。
编译器通过在“inplace”模式下编译函数来实现这一点,该模式使用callees本地结果变量覆盖调用者堆栈帧中的直接堆栈位置,并在被调用者返回后将其保留在那里。
在这种情况下,我们说构造函数已被省略。有关详细信息,请参阅此处:
http://en.wikipedia.org/wiki/Return_value_optimization
如果您在非构造函数上下文中分配fill_list的结果,则返回值为 xvalue (“expiring”值的缩写,类型为对于rvalue),如果目标变量的移动赋值运算符可用,则它将被赋予优先权。
答案 2 :(得分:0)
return语句是一个错误,因为您尝试将rvalue引用(返回类型)绑定到左值(向量res)。右值引用只能绑定到右值。
另外,正如其他人已经提到的,当返回类型是引用类型时返回局部变量是危险的,因为本地对象将在return语句之后被销毁,然后您将获得引用无效对象的引用。
如果要在return语句中避免复制构造,仅使用非引用类型可能已经起作用,因为称为复制省略。你的fill_list函数中的向量res可以直接构造到main函数中的向量myvec中,因此根本不会调用复制或移动构造。但是标准不允许使用此功能,某些编译器不会省略某些复制结构。
答案 3 :(得分:-1)
如果你只是从你的功能中移除&&
它应该可以工作,但它不会作为参考。 fill_list()将创建一个向量并返回它。在返回期间,将创建一个新向量。在fill_list()中创建的第一个向量将被复制到新向量,然后将被销毁。这是复制构造函数的工作。