[轉] c++11 int&& 右值引用
[轉自 http://blog.csdn.net/yuanwang1986/article/details/8666459 ]
C++ 11中引入的右值引用正好可用於標識一個非常量右值。C++ 11中用&表示左值引用,用&&表示右值引用,如:
int &&a = 10
右值引用根據其修飾符的不同,也可以分為非常量右值引用和常量右值引用。
C++ 11中引入的一個非常重要的概念就是右值引用。理解右值引用是學習“移動語義”(move semantics)的基礎。而要理解右值引用,就必須先區分左值與右值。
對左值和右值的一個最常見的誤解是:等號左邊的就是左值,等號右邊的就是右值。左值和右值都是針對表示式而言的,左值是指表示式結束後依然存在的持久物件,右值是指表示式結束時就不再存在的臨時物件。
1 int a = 10 2 int b = 20 3 int *pFlag = &a; 4 vector<int> vctTemp; 5 vctTemp.push_back(1); 6 string str1 = "hello " 7 string str2 = "world" 8 const int &m = 1
請問,a,b, a+b, a++, ++a, pFlag, *pFlag, vctTemp[0], 100, string("hello"), str1, str1+str2, m分別是左值還是右值?
a和b都是持久物件(可以對其取地址),是左值;
a+b是臨時物件(不可以對其取地址),是右值;
a++是先取出持久物件a的一份拷貝,再使持久物件a的值加1,最後返回那份拷貝,而那份拷貝是臨時物件(不可以對其取地址),故其是右值;
++a則是使持久物件a的值加1,並返回那個持久物件a本身(可以對其取地址),故其是左值;
pFlag和*pFlag都是持久物件(可以對其取地址),是左值;
vctTemp[0]呼叫了過載的[]操作符,而[]操作符返回的是一個int &,為持久物件(可以對其取地址),是左值;
100和string("hello")是臨時物件(不可以對其取地址),是右值;
str1是持久物件(可以對其取地址),是左值;
str1+str2是呼叫了+操作符,而+操作符返回的是一個string(不可以對其取地址),故其為右值;
m是一個常量引用,引用到一個右值,但引用本身是一個持久物件(可以對其取地址),為左值。
區分清楚了左值與右值,我們再來看看左值引用。左值引用根據其修飾符的不同,可以分為非常量左值引用和常量左值引用。
非常量左值引用只能繫結到非常量左值,不能繫結到常量左值、非常量右值和常量右值。如果允許繫結到常量左值和常量右值,則非常量左值引用可以用於修改常量左值和常量右值,這明顯違反了其常量的含義。如果允許繫結到非常量右值,則會導致非常危險的情況出現,因為非常量右值是一個臨時物件,非常量左值引用可能會使用一個已經被銷燬了的臨時物件。
常量左值引用可以繫結到所有型別的值,包括非常量左值、常量左值、非常量右值和常量右值。
(1)非const左值引用只能繫結到非const左值;
(2)const左值引用可繫結到const左值、非const左值、const右值、非const右值;
(3)非const右值引用只能繫結到非const右值;
(4)const右值引用可繫結到const右值和非const右值。
大致意思是:模板引數的推導其實就是形參和實參的比較和匹配,如果形參是一個引用型別(如P&),那麼就使用P來做型別推導;如果形參是一個cv-unqualified(沒有const和volatile修飾的)右值引用型別(如P&&),並且實參是一個左值(如型別A的物件),就是用A&來做型別推導(使用A&代替A)。
template <class _Tp> void f(_Tp && { /* do something */ }
template <class _Tp> void g(const _Tp && { /* do something */ }
int x = 123;
f(x); // ok,f()模板函式形參為非const非volatile右值引用型別,實參x為int型別左值,使用int&來做引數推導,因此呼叫f<int &>(int &
f(456); // ok,實參為右值,呼叫f<int>(int &&
g(x); // error,g()函式模板引數為const右值引用型別,會呼叫g<int>(const int && ,通過右值引用規則四可知道,const右值引用不能繫結到左值,因此會導致編譯錯誤