c++ - 从右值引用限定的成员函数按值或按右值引用返回?
问题描述
在Effective Modern C++的第 12 条中,Scott Meyers 编写了以下类来展示在引用限定符上重载成员函数是多么有用:
class Widget {
public:
using DataType = std::vector<double>;
…
DataType& data() & // for lvalue Widgets
{ return values; } // return lvalue
DataType data() && // for rvalue Widgets
{ return std::move(values); } // return rvalue
…
private:
DataType values;
};
这似乎很清楚: nownon_temp_obj.data()
将调用第一个重载并返回对之后仍然存在的对象成员的引用,而make_temp_obj().data()
通过值返回对象的成员,该成员在该表达式完成后立即死亡。
这是我的第一个问题:关于&&
重载,考虑到我们按值返回,为什么return std::move(values);
不只是?return values;
然而,在勘误表中,迈耶斯写道
让成员函数的右值引用重载
data
返回一个右值的更好方法是让它返回一个右值引用。这将避免为返回值创建临时对象,并且与data
第 84 页顶部附近的原始接口的按引用返回一致。
我解释为建议改变
DataType data() &&
{ return std::move(values); }
到
DataType&& data() &&
{ return std::move(values); }
但我不明白原因,特别是鉴于这个答案几乎让我相信这本书的版本是正确的,而勘误表是错误的。
所以我的第二个问题是:谁是对的?
解决方案
values
is an object member and an lvalue, so if you just return values
directly, it will be copied to the return value, not moved. The point of the &&
ref-qualified overload is to avoid making an unnecessary copy. return std::move(values)
accomplishes this by casting values
to an rvalue, so that it gets moved from instead of copied.
For the second part of your question: both have their advantages and disadvantages. As the answer you linked notes, returning by value from the &&
overload avoids lifetime issues, since the returned object will have its lifetime extended if a reference is immediately bound to it. On the other hand, returning by value could destroy the value of values
unexpectedly. For instance:
DataType Widget::data() &&
{ return std::move(values); }
void func() {
Widget foo;
std::move(foo).data(); // Moves-constructs a temporary from
// foo::value, which is then immediately
// destroyed.
auto bar = foo.data(); // Oops, foo::value was already moved from
// above and its value is likely gone.
}