美文网首页C++复习
C11新特性右值引用&&

C11新特性右值引用&&

作者: 凉拌姨妈好吃 | 来源:发表于2018-04-28 02:27 被阅读112次
    首先我们先明白一个概念:什么是左值?什么是右值?
    • 左值:既能出现在等号左边也能出现在等号右边的变量或表达式
    • 右值:因为声明结束后会被销毁,所以不能放在等号左边

    c中,左值其实就是有名字的变量,而运算操作(加减乘除,函数返回值等)就是右值,右值不允许被修改
    c++中,相对于右值引入了一个新的概念,基础类型右值不允许被修改,但用户自定义的类型,右值可以通过它的成员函数进行修改

    类的右值是一个临时对象,如果没有被绑定到引用,在表达式结束时就会被废弃。于是我们可以在右值被废弃之前,移走它的资源进行废物利用,从而避免无意义的复制。被移走资源的右值在废弃时已经成为空壳,析构的开销也会降低。

    class People {
    public:
      People(string name) 按值传入字符串,可接收左值、右值。接收左值时为复制,接收右值时为移动
      : name_(move(name))  显式移动构造,将传入的字符串移入成员变量
      {
      }
      string name_;
    };
    
    People a("Alice");  移动构造name
    
    string bn = "Bob";
    People b(bn);  拷贝构造name
    
    
    int a = 3;
    int b = 2; //此时a b都是左值
    int c = a+b;//此时a+b就是右值,a+b产生了临时的变量,在表达式结束之后就会被销毁
    
    那么什么是&,什么是&&

    &是c++里的左值引用
    &&是c11里的右值引用
    左值引用就不多说了,现在解释一下右值引用(来自知乎[hggg ggg])

    string a = string("w")+"o"+"r"+"l"+"d";       -----1
    //先看一下c11里的string的重载+
    string operator+ (const string&& lhs, const string&& rhs);
    //而c98里的string重载+为
    string operator+ (const string& lhs, const string& rhs);
    //c98在执行1这条语句的时候
    //因为不能在原本w的基础上进行+(为了不把w的值修改)
    //编译器会开辟一个新的内存来存储临时对象
    
    //c11执行1这条语句的时候
    //因为右值在语句执行结束后就会被销毁
    //所以直接在原本w的基础上进行+,而不用新建临时对象
    从这里可以看出,右值引用有利于减少开销
    
    关于右值的生命周期
      string Proc()
      {
           return string("abc");
      }
       
       int main()
      {
          const string& ref = Proc();
          //此时右值的生命周期延长了,直到main函数结束
          cout << ref << endl;
          return 0;
      }
    
    • 右值只能被const引用指向,在这时,右值的生命周期被延长了,直到引用销毁。
    • 因为右值只能被const引用指向,所以我们才会在拷贝构造函数和赋值函数形参内加上const(还有一个原因是避免参数被修改),这里c11出现了一个特殊智能指针的non const拷贝构造函数
    class auto_ptr
    {
       public:
           auto_ptr(auto_tr& p)
            {
                 ptr_ = p.ptr_;
                 p.ptr_ = NULL;//因为需要修改p的值,所以不能用const
            }
        private:
             void*  ptr_;
    };
    auto_ptr("h"+"i");//编译出错,因为"h"+"i"生成的临时变量不能指向non const参数
    
    如何使上面的临时变量能够被non const引用指向?

    使用std::move()接受一个参数,返回该参数对应的右值引用
    move调用告诉编译器:我们有一个左值,但我们希望像一个右值一样处理它。我们必须认识到,调用move就意味着承诺:除了对rr1赋值或销毁它外,我们将不再使用它在调用move后,我们不能对移后源对象的值做任何假设。我们可以销毁一个移后源对象,也可以赋予它新值,但是不能使用一个移后源对象的值。

    //下面是move的源码
    template<typename _Tp>  
      inline typename std::remove_reference<_Tp>::type&&  move(_Tp&& __t)  
      { 
        return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); 
      }  
    //所以我们只需要将上面的要传入auto_ptr的参数先经过move转换成右值引用
    
    //下面有一个move使用的小例子
    void swap(T& a, T& b)
     {
          T tmp = move(a);
          a = move(b);
          b = move(tmp);
          //可以看出相比较之前的进行了多次资源拷贝、销毁的swap
          //在swap里使用move只是进行了三次的指针交换,效率提升
     }
    
    move源码中使用了static_cast,它是什么?

    static_cast是一个强制类型转换符,强制类型转换会告诉编译器:我们知道并且不会在意潜在的精度损失。
    如果没有使用强制类型转换符,那么编译器会产生警告
    static_cast最神奇的地方在于它可以找回存在于void * t 中的值,如下

    double a = 3.14;
    void *pv = &a;
    //我们不知道void的地址中存放的是什么类型的对象
    //对于void而言,它的内存空间仅仅是内存空间,无法访问内存空间里的对象
    double *b = static_cast<double*> (pv);
    //强制转换的结果与原始的地址值相等
    
    了解了什么是move,那么forward()是什么?

    forward()接收一个参数,返回该参数本来所对应的类型的引用。(即完美转发)

    void outer(T&& t) {};
    void fun(){};
    outer(fun());  
    //fun为右值,但是因为outer的参数有参数名,所以在outer内部,它永远是左值
    //为了解决这个问题,就提出了forward()函数(完美转发)
    //forward()的源码
    template<typename _Tp> inline _Tp&& forward(typename std::remove_reference<_Tp>::type& __t)   
      { 
        return static_cast<_Tp&&>(__t); 
      }  
      
    template<typename _Tp> inline _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t)   
      {  
        static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"  " substituting _Tp is an lvalue reference type");  
        return static_cast<_Tp&&>(__t);  
      }  
    
    如何使用forword呢?
    struct X {};  
    void inner(const X&) {cout << "inner(const X&)" << endl;}  
    void inner(X&&) {cout << "inner(X&&)" << endl;}  
    template<typename T>  
    void outer(T&& t) {inner(forward<T>(t));}  
      
    int main()  
    {  
        X a;  
        outer(a);  
        outer(X());  
        inner(forward<X>(X()));  
    }  
    //inner(const X&)  
    //inner(X&&)  
    //inner(X&&)  
    

    相关文章

      网友评论

      本文标题:C11新特性右值引用&&

      本文链接:https://www.haomeiwen.com/subject/bzbblftx.html