move 右值 折叠引用

 

引用是啥:
    int &r; //error . 需要定义初始化

    int &r = 变量; //ok

    引用相当于一个常指针 : int &r == int *const r; 
    

右值简介:

一定要区分右值引用变量(左值)与右值(临时)

    int i = 10;
    int& r = i; //ok
    int&& rr2 = 55; //ok ; 引用了字面量
    int&& rr = i ; //error . 右值引用变量只能绑定字面量或临时对象;

    int& lr = rr2; //ok , 左值变量可以绑定左值. rr2是一个左值 ; 相当于 int *const lr = &rr2;

    

    
    //这个很重要!!! 虽然rr2是右值引用, 但本身还是一个左值变量
    int&& rr3 = rr2; //error , rr2是一个左值, rr3无法绑定到一个左值;

    //这行需要注意. 可以强制类型转换: std::move , move实现就是static_cast;
    //对这行代码的理解 将影响你理解 std::forward
    int&& rr3 = std::move(r); // ok . 相当于static_cast<int&&>(r);

对于右值最需要理解的是下面 2 行代码 :
int &&rr3 = rr2 // error
int &&rr3 = std::move(rr2) //ok

为什么第一行出错, 第二行可以?
1.右值引用的核心是 只能绑定一个无名的临时对象. 因为一旦有个名字(即变量名) 就说明这是一个左值;
  rr2 虽然是一个右值引用, 但其本身还是一个变量 即左值.
  所以 int &&rr3 无法绑定;

2.通过std::move 转换后无论什么类型都是一个右值即一个无名的临时对象,
  所以 int &&rr3 可以绑定;

一定要区分什么是右值(临时对象)与右值引用变量(即左值)

int &&rr 是一个右值引用变量, 是一个左值

std::move(rr) 返回的是一个右值引用, 是一个临时对象

总之: 右值引用变量(int &&rr) = 临时对象 ;



 

先了解一个东西,折叠引用 :

即如果模板类型参数是一个右值引用, 如果传递进来的是一个左值,则实例化的类型参数是一个左值引用;

先下结论: 对于模板参数是 T&& , 既可以传递左值,也可以右值;

template <typename T>
void test_ref(T&& ){}

如上模版, 参数是右值引用 . 

那么 test_ref(string("123")) : T == string
               test_ref(123) : T == int

如此调用是正确的

***** 但右值引用在模板中有例外 , 此时如果传递了一个左值 ,将产生折叠引用

看一下类型推断:

string s1 = "1";

test_ref(string("nihao")) // T == string, 正常的函数参数推断;此实参是一个右值

test_ref(s1)  //模板中的例外 . 此时,传递的是一个左值,那么 T 是string& , 加上后面的&& => string&&& ,折叠 => string&;

由于s1 是一个左值. 此时 T == string& ;

由此可推断: 原型是  test_ref<string&>(string& &&) .

string& && 由折叠引用规则 -> string&


最后函数原型是 : void test_ref<string&>(string&); 


所以当一个左值传递到 形参是右值的模板函数中, 此时将产生折叠引用 . 

总之: 对一个T&& 的模板参数来说, 如果是左值则实例化成 T& ;


 

关于move  , 其实是 static_cast<Type&&>(obj);  使用强制类型转换把左值或右值转成右值; 意思是无论什么类型返回的总是一个匿名的右值对象;

比如:

    string ss = "nihaoaa";
    string ss1 = "hi";
    ss1 = static_cast<string&&>(ss);  
    cout << "ss:" << ss << ",ss1:" << ss1 << endl;

//完整的例子


/*
    与std::move 的实现一样;
    注意: 参数类型是 T&&
    
    由于模板类型参数是右值引用, 所以如果传递的是左值(变量) ,则实例化后是一个左值引用
*/


template <typename Type>
typename remove_reference<Type>::type&& move_ref(Type&& obj)
{
    return static_cast<typename remove_reference<Type>::type&&>(obj);
}


//main.cpp


    string s1 = "hey fucker";
    string s2 = "1234";
    /*

        第一次实例化 :
        由于s1 是一个左值(变量)
        产生引用折叠:
            当模版参数是 一个右值引用的话(Type&& ) .如果实参是左值. 则产生折叠即:
            Type == string&  ,  再加上 && => string&&& => string&


        remove_reference<string&>::type&& move_ref(string& obj)
        {
            return static_cast<remove_reference<string&>::type&&>(obj);
        }
        由于 remove_reference<string&>::type 可知  type == string;

        原型: string&& move_ref(string& obj){
                    return static_cast<string&&>(obj);
                }
    */

    s2 = move_ref(s1); // string.operator(string&& ); move_ref (move) 返回的是一个匿名的临时对象
    cout << "s1:" << s1 <<" , s2:"<< s2 << endl;



    /*
        第二次实例化:
         由于 string("end") 是一个 临时对象 即 右值;
         所以 Type 是 string:

        remove_reference<string>::type&& move_ref(string&& obj)
        {
            return static_cast<remove_reference<string>::type&&>(obj);
        }
        remove_reference<string>::type -> type 是 string;

        原型:
         string&& move_ref(string&& obj) {
            return static_cast<string&&>(obj);
         }
    */
    s2 = move_ref(string("end")); // string.operator(string&& );
    cout << "s1:" << s1 <<" , s2:"<< s2 << endl;

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值