zoukankan      html  css  js  c++  java
  • 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;
    }

    重新看了一遍C++11的书,关于引用类型感觉收获了很多。

    1.因为我们使用引用类型的变量,就跟使用所引用类型的变量一样,导致我忘了引用类型是一种类型。比如:

    int  a = 1;
    int& b = a;
    int& c = b;
    b = c;

    自从b被初始化后,用起来它就是一个int的变量一样。实际上,它确实一个int的引用类型,而不是int。

    2.关于C++11中模板的参数类型推断,有两个特殊情况。

    template<typename T> void function(T&& a);

    如果传给function的实参是一个左值,则T被推断为T&,而不是T。这是第一个特殊情况。

    如果T& && 被折叠为T&,这是第二个特殊情况。

  • 相关阅读:
    day4 流程控制while 判断if
    作业2
    C语言I博客作业02
    ActionScript3与PHP的通信
    WordPress代码和分析从主题开始
    事件、委托、异步
    201920201学期 20192405《网络空间安全专业导论》第二周学习总结
    201920201学期 20192405《网络空间安全专业导论》第二周学习总结
    201920201学期 20192405《网络空间安全专业导论》第四周学习总结
    201920201学期 20192405《网络空间安全专业导论》第一周学习总结
  • 原文地址:https://www.cnblogs.com/wangpei0522/p/4472548.html
Copyright © 2011-2022 走看看