C++11函数返回右值引用

我们定义了一个可以传入右值引用的构造函数的类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;
}

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。