引用是啥:
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;