根据我的理解,返回rvalues对函数的引用是危险的原因是由于以下代码:
T&& f(T&& x) { do_something_to_T(x); return static_cast<T&&>(x); }
T f(const T& x) { T x2 = x; do_something_to_T(x2); return x2; }
T&& y = f(T());
这会将y
留作未定义的悬空参考。
但是,我不明白为什么上面的代码甚至编译?是否有合理的理由将rvalue引用分配给另一个右值引用?粗略地说,rvalues不是“临时”,即在表达结束时会变得无效吗?能够分配它们对我来说似乎很愚蠢。
答案 0 :(得分:8)
粗略地说,rvalues不是“临时”,即在表达结束时会变得无效吗?
不,他们不是。
鉴于您的功能f
,这同样合法:
T t{};
T&& y = f(std::move(t));
这是完全有效的C ++ 11代码。它也很明确地发生了什么。
您的代码未定义的唯一原因是您传递了一个临时代码。但r值参考不一定是临时参考。
再详细说明一下,r值引用在概念上是对某些值的引用,某些操作被认为是可以做的,否则就不行了。
在C ++ 11中非常仔细地指定了R值引用。 l值引用可以绑定到任何非临时引用而无需强制转换或任何内容:
T t{};
T &y = t;
r值引用只能隐式绑定到临时或其他“xvalue”(在不久的将来肯定会消失的对象):
T &&x = T{};
T &&no = t; //Fail.
为了将r值引用绑定到非x值,您需要进行显式转换。 C ++ 11拼写此演员的方式是:std::move
:
T &&yes = std::move(t);
我所说的“某些操作”是“移动”。可以在两个条件下从一个对象移动:
这些是r值引用可以绑定到某些内容的唯一两种情况。
存在r值引用的原因主要有两个:支持移动语义并支持完美转发(需要一种新的引用类型,它们可以将时髦的转换机制挂钩,以及可能移动语义)。因此,如果您没有执行这两项操作中的一项,则使用&&
的原因可疑。
实际上,我甚至会在两种情况下说参数应该是&&
:转发函数或移动构造函数/赋值。在所有其他情况下,如果您想允许移动,则按值而不是&&
进行输入。这样,您可以强制移动作为函数调用的一部分发生。对象可以从那里移动到最终目的地。