左值與右值
左值是這樣一種表達式,它指向一塊物理內存,並允許我們通過運算符&來取得這塊內存的地址,而右值則是非左值的表達式。常量和匿名的臨時變量都爲右值,如函數返回值。
比如:int a=0; //a爲左值
string s="ss";//s爲左值,"ss"爲右值
string ss=string("e");//ss爲左值,string("e")爲右值
左值引用與右值引用
右值引用
和左值引用
都是引用,都是一個變量(即都是一個左值),左值引用通過在類型名後加 &
來表示,而右值引用則通過在類型名後加 &&
表示。只不過左值引用引用的是左值,而右值引用只能引用右值。函數形參都是左值,因爲函數形參都有名稱,都可以對形參進行取地址操作。
右值與右值引用
右值不是左值,右值引用是左值,是對右值的一種引用。c++11引入右值引用主要是爲了實現移動語義和完美轉發。右值引用本身沒什麼意義。
std::forward與std::move
std::forward實現
template<class TPYE>
TPYE&& forward(typename remove_reference<TPYE>::type& a) noexcept
{
return static_cast<TPYE&&>(a);
}
先看remove_reference的實現,顧明思議就是去掉引用:
template< class T > struct remove_reference {typedef T type;};
template< class T > struct remove_reference<T&> {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};
std::forward實現了完美轉發看下面的代碼(參考鏈接:https://blog.csdn.net/victorydh123/article/details/73603830):
void f(int&& a) {
std::cout << "rvalue" << std::endl;
}
void f(int& a) {
std::cout << "lvalue" << std::endl;
}
template<typename T>
void test(T&& a) {
f(forward<T>(a));//如果沒有forward 將永遠調用左值版本,因爲右值引用是左值。右值引用的形參只能接受右值。完美轉發實現了傳遞進來什麼類型,我就返回什麼類型。其實就是解決了右值引用形參傳入後,形參本身已經是一個左值了,不能作爲下一個函數的右值引用參數。
}
template<typename T>
T&& forward(std::remove_reference_t<T>& arg)
{
return static_cast<T&&>(arg);
}
int main()
{
int a = 10;
test(a);//"lvalue"
test(std::move(a));//"rvalue"
//system("pause"); //for windows VS to pause to see the output
return 0;
}
模板推斷時,引用的摺疊規則:
& & -> &
& && -> &
&& & -> &
&& && -> &&
當參數是一個左值時,在模板類型推斷的時候,左值會被推斷成左值引用,不管傳遞的參數是左值類型還是左值引用,通通變成左值引用。
std::move的實現代碼也很簡單
template<typename _Tp>
inline typename std::remove_reference<_Tp>::type&& move(_Tp&& __t)
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
就是將__t轉爲右值引用返回。
看上去move什麼也沒做,沒錯std::move並不產生新的對象,不會調用構造函數,只是做了一個類型轉換,這樣做的目的主要是爲了支持移動構造和移動賦值,因爲移動構造和移動賦值的參數類型爲右值引用。
要了解移動構造和移動賦值請百度“移動構造和移動賦值”或者參考這篇博客:https://www.cnblogs.com/ldlchina/p/6608154.html