帮手如果你看get
,辅助功能为std::tuple
,你会发现以下过载:为什么得到的std ::的元组返回右值引用,而不是值
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >&&
get(tuple<Types...>&& t);
换句话说,它返回当输入元组是右值引用本身时的右值引用。为什么不按价值回报,在函数体中调用move
?我的观点如下:get的返回值将被绑定到一个引用或者一个值(它可以被绑定到我想象的任何东西,但这不应该是一个常见的用例)。如果它被绑定到一个值,那么无论如何都会发生移动建设。所以你不会因为回报而损失任何东西。如果你绑定到一个引用,那么返回一个右值引用实际上可能是不安全的。为了显示一个例子:
struct Hello {
Hello() {
std::cerr << "Constructed at : " << this << std::endl;
}
~Hello() {
std::cerr << "Destructed at : " << this << std::endl;
}
double m_double;
};
struct foo {
Hello m_hello;
Hello && get() && { return std::move(m_hello); }
};
int main() {
const Hello & x = foo().get();
std::cerr << x.m_double;
}
当运行时,该程序打印:
Constructed at : 0x7ffc0e12cdc0
Destructed at : 0x7ffc0e12cdc0
0
换言之,X是立即悬空参考。而如果你只是这样写foo:
struct foo {
Hello m_hello;
Hello get() && { return std::move(m_hello); }
};
这个问题不会发生。此外,如果你再使用FOO这样的:
Hello x(foo().get());
它似乎并不像有任何额外的开销是否通过值或右值引用返回。我测试过这样的代码,看起来它会一直只执行一次移动构建。例如。如果我添加一个成员:
Hello(Hello &&) { std::cerr << "Moved" << std::endl; }
我构建X如上,我的程序只“感动”一次不管我是否通过值或右值引用返回打印。
有没有很好的理由我错过了,或者这是一个疏忽?
注:这里有一个很好的相关问题:Return value or rvalue reference?。似乎认为在这种情况下价值回报通常是可取的,但STL出现的事实让我很好奇STL是否忽略了这种推理,或者如果他们有特殊的理由可能不适用通常。
编辑:有人提出这个问题是Is there any case where a return of a RValue Reference (&&) is useful?的重复。不是这种情况;这个答案建议通过右值引用返回,作为避免数据成员复制的一种方式。正如我上面详细讨论的那样,如果您首先拨打move
,那么无论您是按价值还是按右值参考,复制都将被忽略。
这不是远程重复。我的答案明确而详细地讨论了如何通过值返回并事先调用std :: move实现与从数据成员移动相同的效果。我的问题与两种方法的差异有关。我引用自己的问题与你引用的问题有更多的关系。在急于标注重复的内容之前,请仔细阅读。 –
如果您按值返回 - 您创建一个副本。如果您移动它,则移动副本。如果你返回一个r值引用,你可以移动它,然后移动原始对象,或者不用移动字体处理它,那么它的行为就像常规引用。问题是如果你返回一个r值参考,你不会强制复制。 –
为什么需要移动可施工性? – Columbo