1.左值與右值
最感性的認識。
當然,左值也是可以在右邊的。
左值是可以被修改的,右值不能。
當然取地址也是。
生存週期一般左值會比右值的長,一般右值都計算時產生的無名臨時物件,存在時間比較短。
下面還有一種情況也要區分。
2.左值引用和右值引用
左值引用:可以引用一個物件,有時候也可以繫結一個右值。
右值引用:只能引用右值。
1左值引用示例
看以下程式碼,比較正常。
int a = 3;
int &p1 = a; // 左值引用
若左值引用右值將報錯
但加上const就可以引用了
2右值引用示例
不能把左值繫結到右值,但使用move可以把左值轉換右值就可以繫結
示例1
示例2
示例3
理解了上邊知識,接下來是引用摺疊規則,
3.引用摺疊
先看演示程式碼
#include <iostream>
using namespace std;
using lRef = int&; //左值引用
using rRef = int&&; //右值引用
int main(int argc, char **argv)
{
is_lvalue_reference<lRef &>::value ?
cout << "lRef & 左值引用" << endl :
cout << "lRef & 右值引用" << endl;
is_lvalue_reference<lRef &&>::value ?
cout << "lRef && 左值引用" << endl :
cout << "lRef && 右值引用" << endl;
is_rvalue_reference<rRef &>::value ?
cout << "rRef & 右值引用" << endl :
cout << "rRef & 左值引用" << endl;
is_rvalue_reference<rRef &&>::value ?
cout << "rRef && 右值引用" << endl :
cout << "rRef && 左值引用" << endl;
return 0;
}
除錯結果
這就是引用摺疊規則。
這怎麼理解呢,看下圖
可以看到只有都是右值引用的時候才是右值引用,當然只有一個右值引用的情況下自然而然也是右值引用。
這是就引用摺疊。
4.完美轉發
主要用於引數轉發時是左值傳入還是右轉入
考慮以下程式碼
#include <iostream>
using namespace std;
template<typename T>
void Fun1(T& v)
{
cout << "左值引用呼叫" << v << endl;
}
template<typename T>
void Fun1(T&& v)
{
cout << "右值引用呼叫" << v <<endl;
}
template<typename T>
void Fun(T&& v)
{
Fun1(v);
}
int main(int argc, char **argv)
{
int a = 3;
Fun(a);
return 0;
}
主函式裡給Fun傳入a,根據上邊知識,a是一個左值,看除錯結果是呼叫哪一個過載版本Fun1
結果跟預想的一樣,接下來更改換右值傳入。
int main(int argc, char **argv)
{
Fun(5);
return 0;
}
除錯結果
我們發現跟想象中不一樣!!!
這時候該怎麼辦。
c++11中提供了一個用於完美轉發的函式forward。
還提供了一個move函式,用於把左值變成右值的方法。
forward會根據引用摺疊規則得出傳入的是左值引用還是右值引用
接下來只需更改一下Fun函式,其他的不變
template<typename T>
void Fun(T&& v)
{
Fun1(forward<T>(v));
}
除錯結果
發現跟我們預想一樣了。
完美轉發完整示例
#include <iostream>
using namespace std;
template<typename T>
void Fun1(T& v)
{
cout << "左值引用呼叫" << v << endl;
}
template<typename T>
void Fun1(T&& v)
{
cout << "右值引用呼叫" << v <<endl;
}
template<typename T>
void Fun(T&& v)
{
Fun1(forward<T>(v));
}
int main(int argc, char **argv)
{
int a = 1;
Fun(a);
Fun(move(a));
const int b = 2;
Fun(b);
Fun(move(b));
Fun(5);
return 0;
}
除錯結果
5.結語
學無止境。
---End