标签:
我们定义了一个可以传入右值引用的构造函数的类B,在使用std::move的时候,我们非常容易犯一个错误。看下面的代码:
class B { public: B() :s(10), ptr(new int[s]) { std::cout << "default constructor" << std::endl; for (int i = 0; i < s; i++) { ptr[i] = i; } } B(const B& b) :s(b.s) { std::cout << "B&b" << std::endl; for (int i = 0; i < s; i++) ptr[i] = b.ptr[i]; } B(B&& b) :s(b.s), ptr(b.ptr) { std::cout << "B&& b" << std::endl; b.ptr = 0; } ~B() { std::cout << "~B()" << std::endl; if (ptr) delete[] ptr; } friend std::ostream& operator <<(std::ostream& os, B& b); private: int s; int* ptr; }; std::ostream& operator <<(std::ostream& os,B& b) { os << "[ "; for (int i = 0; i < b.s; i++) os << b.ptr[i] << " "; os << "]" << std::endl; return os; } B&& f()//注意这里B&& { B tmp; return std::move(tmp); } B& f2()//注意这里的B& { B t; return t; } int main() { B b0(f2()); B b1(f()): return 0 ; }
函数f2返回B的引用,但是B是一个临时对象,马上就会被析构,b0的构造函数传入的参数是一个已经被析构的对象!大家能够非常容易就看出错误所在。
但是,函数f返回std::move(tmp),直观不容易发现问题。
展开函数f():
首先一个B的临时对象tmp被创建,使用std::move(tmp),将tmp由左值变为右值的引用,它是右值。编译创建一个临时对象B&& _tmp = std::move(tmp)。_tmp被传递给对象b1的构造函数。
无论是右值引用还是左值引用,它们都是引用。所以,f()和f2()发生了同样的错误!
我们可以这样修改:
B f() { B tmp; return std::move(tmp); }
展开函数f():
首先一个B的临时对象tmp被创建,使用std::move(tmp),将tmp由左值变为右值的引用,它是右值。编译创建一个临时对象B _tmp = std::move(tmp)。_tmp调用构造函数B(B&&),转移tmp的空间和大小。_tmp被传递给对象b1的构造函数。
以上。
再附上几个容易混淆的代码:
int& f1(int& a) { return a; }//正确,返回a的引用 int main() { int&& i = 123; int&& a = i;//错误,不能将左值赋给右值引用。如果你犯了这个错误,请务必思考清楚:引用对象在完成初始化后,这个对象的类型是什么。提示,i是一个具名引用。 int&& b = std::move(i);//正确 return 0; }
标签:
原文地址:http://www.cnblogs.com/wangpei0522/p/4472548.html