从右值引用限定成员函数按值或按右值引用返回?
Returning by value or by rvalue reference from rvalue reference qualified member function?
在 Effective Modern C++ 的第 12 项中,Scott Meyers 写了以下内容 class 以展示在引用限定符上重载成员函数的好处:
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;
};
这似乎很清楚:现在 non_temp_obj.data()
将调用第一个重载和 return 对之后仍然存在的对象成员的引用,而 make_temp_obj().data()
returns 按值是一个对象的成员,该成员在该表达式完成后立即死亡。
这是我的第一个问题:关于 &&
过载,为什么 return std::move(values);
而不仅仅是 return values;
,考虑到我们 return价值?
然而,迈耶斯在 errata 中写道
A better way to have the rvalue reference overload of the data
member function return an rvalue is to have it return an rvalue reference. That would avoid the creation of a temporary object for the return value, and it would be consistent with the by-reference return of the original data
interface near the top of page 84.
我将其解释为建议更改
DataType data() &&
{ return std::move(values); }
至
DataType&& data() &&
{ return std::move(values); }
但我不明白其中的原因,尤其是 this answer 这几乎让我相信书的版本是正确的,勘误表是错误的。
所以我的第二个问题是:谁是对的?
values
是一个对象成员,也是一个左值,所以如果你直接return values
,它会被复制到return值,不会移动。 &&
ref-qualified 重载的要点是避免制作不必要的副本。 return std::move(values)
通过将 values
转换为右值来实现这一点,这样它就被移动而不是被复制。
对于你问题的第二部分:两者各有优缺点。作为您链接注释的答案,returning 按 &&
重载的值避免生命周期问题,因为如果引用立即绑定到 returned 对象,其生命周期将延长。另一方面,按值 returning 可能会意外地破坏 values
的值。例如:
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.
}
在 Effective Modern C++ 的第 12 项中,Scott Meyers 写了以下内容 class 以展示在引用限定符上重载成员函数的好处:
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;
};
这似乎很清楚:现在 non_temp_obj.data()
将调用第一个重载和 return 对之后仍然存在的对象成员的引用,而 make_temp_obj().data()
returns 按值是一个对象的成员,该成员在该表达式完成后立即死亡。
这是我的第一个问题:关于 &&
过载,为什么 return std::move(values);
而不仅仅是 return values;
,考虑到我们 return价值?
然而,迈耶斯在 errata 中写道
A better way to have the rvalue reference overload of the
data
member function return an rvalue is to have it return an rvalue reference. That would avoid the creation of a temporary object for the return value, and it would be consistent with the by-reference return of the originaldata
interface near the top of page 84.
我将其解释为建议更改
DataType data() &&
{ return std::move(values); }
至
DataType&& data() &&
{ return std::move(values); }
但我不明白其中的原因,尤其是 this answer 这几乎让我相信书的版本是正确的,勘误表是错误的。
所以我的第二个问题是:谁是对的?
values
是一个对象成员,也是一个左值,所以如果你直接return values
,它会被复制到return值,不会移动。 &&
ref-qualified 重载的要点是避免制作不必要的副本。 return std::move(values)
通过将 values
转换为右值来实现这一点,这样它就被移动而不是被复制。
对于你问题的第二部分:两者各有优缺点。作为您链接注释的答案,returning 按 &&
重载的值避免生命周期问题,因为如果引用立即绑定到 returned 对象,其生命周期将延长。另一方面,按值 returning 可能会意外地破坏 values
的值。例如:
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.
}