最近我一直在仔细研究C++的分类,左值和右值的区别看起来很清楚,但是当谈到右值和右值的时候我就搞混了。
举个例子:
#include <iostream>
using std::cout;
using std::endl;
using std::move;
class Type {
public:
int value;
Type(const int &value=0) :value(value) {}
Type(const Type &type) :value(type.value){}
Type(Type &&type) noexcept :value(type.value) {}
Type &operator= (const Type &type) {
value = type.value;
return *this;
}
Type &operator=(Type &&type) noexcept{
value = type.value;
return *this;
}
};
Type foo1(const Type &value) {
return Type(value);
}
Type &&foo2(const Type &value) {
return Type(value);
}
int main() {
Type bar1(123);
cout << foo1(bar1).value << endl;
cout << foo2(bar1).value << endl;
Type bar2;
bar2 = foo1(bar1);
cout << bar2.value << endl;
bar2 = foo2(bar1);
cout << bar2.value << endl;
return 0;
}
运行该示例,控制台将:
**123人
123
123
-858993460**有人能解释一下为什么它在最后一个输出中给出了一个意外的值吗?
这个例子展示了xvalue的什么特性?
2条答案
按热度按时间2hh7jdfx1#
foo2
正在返回绑定到临时的引用,该临时被立即销毁;它总是返回一个悬挂引用。不扩展return语句中函数返回值的临时绑定:它在返回表达式的末尾立即被销毁。2这样的函数总是返回一个悬空引用。
对返回的引用(如
foo2(bar1).value
和bar2 = foo2(bar1);
)解引用导致UB;一切皆有可能。另一方面,
foo1
没有这个问题,返回值是从临时对象中移出的。fykwrbwg2#
这里有一个非常简单的解释,考虑一下C ++0x之前的教科书例子:
这个代码片段崩溃了,你并不感到惊讶,对吧?你返回了一个引用(实际上,它只不过是一个美化的指针)到一个本地对象,而这个对象在函数返回之前就被销毁了,因此
val
注定要成为一个悬空引用。现在考虑一下:
就对象的生命周期而言,这和以前完全一样,右值引用并不是某种神奇的移动对象的全新工具,它是相同的老引用(读作:美化指针)。没有任何变化-你仍然返回了一个被销毁对象的地址。
我假设每个人都同意
std::move
只不过是一个美化了的static_cast<T&&>
,所以如果使用它,结果是一样的:本例与上例有99.9%相同。
(The 0.1%的差异是,例如,在前两个例子中,GCC知道代码出错了,并且实际上返回了一个空引用,该空引用保证在第一次使用时崩溃;而在最后一个例子中,由于
std::move
是一个函数,所以它不能确定,所以它顺从地返回坏地址)。