据我了解,从函数返回右值引用是危险的原因是由于以下代码:
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
成为未定义的悬空引用。
但是,我不明白为什么上面的代码甚至可以编译?是否有合理的理由将一个右值引用分配给另一个右值引用?粗略地说,右值不应该是“临时的”,即在表达式末尾无效吗?能够分配它们对我来说似乎很愚蠢。
粗略地说,右值不是应该是“临时的”,即在表达式末尾无效吗?
不,他们不是。
鉴于您的功能
f
,这同样合法:
T t{};
T&& y = f(std::move(t));
这是完全有效的 C++11 代码。而且它也明确定义了会发生什么。
您的代码未定义的唯一原因是您传递了一个临时值。但右值引用不必是临时的。
更详细地说,从概念上讲,右值引用是对某个值的引用,在该值中某些操作被认为可以执行,否则这些操作是不可以执行的。
C++11 中非常仔细地指定了 R 值引用。左值引用可以绑定到任何非临时引用,而不需要强制转换或任何其他操作:
T t{};
T &y = t;
右值引用只能隐式绑定到临时或其他“x值”(一个肯定会在不久的将来消失的对象):
T &&x = T{};
T &&no = t; //Fail.
为了将右值引用绑定到非x值,您需要进行显式强制转换。 C++11 拼写此转换的方式很能说明问题:
std::move
:
T &&yes = std::move(t);
我所说的“某些操作”是“移动”。在以下两个条件下可以从物体上移动:
这是右值引用可以绑定到某些东西的唯一两种情况。
右值引用存在的原因有两个:支持移动语义和支持完美转发(这需要一种新的引用类型,可以将时髦的转换机制挂钩,以及潜在的移动语义)。因此,如果您不执行这两个操作之一,那么使用
&&
的理由是值得怀疑的。