C++98中規定了左值和右值的概念,但是一般程序員不需要理解的過於深入,因爲對於C++98,左值和右值的劃分一般用處不大,但是到了C++11,它的重要性開始顯現出來。
C++98標準明確規定:
左值是可以取得內存地址的變量。
非左值即爲右值。
從這裏可以看出,可以執行&取地址的就是左值,其他的就是右值。
這裏需要明確一點,能否被賦值不是區分C++左值和右值的區別。
我們給出四個表達式:
string one("one"); const string two("two"); string three() { return "three"; } const string four() { return "four"; }
這裏四個變量表達式,後面兩個是臨時變量,不可以取地址,所以屬於右值,前面兩個就是左值。
這裏寫出一個函數:
void test(const string &s) { cout << "test(const string &s):" << s << endl; }
然後進行測試:
test(one);
test(two);
test(three());
test(four());
編譯我們發現,這個test可以接受所有的變量。
我們使用另一個函數做測試:
void test(string &s) { cout << "test(string &s):" << s << endl; }
然後測試發現,只有one可以通過調用。
然後我們同時提供兩個test函數,然後我們編譯運行發現:
test(string &s):one test(const string &s):two test(const string &s):three test(const string &s):four
所以我們可以得出結論:
在C++中,const X&可以接受所有的變量
X &只可以接受普通變量
同時我們可以看出C++重載決議的一個特點:
當一個參數可以匹配多個函數時,總是匹配最精準的一個。
例如上面的one,它也可以接受const X&,但是當X&形式的參數存在時,立刻選擇後者。顯然後者是專門爲其準備的,二者的語義最爲符合。X&包含有一種修改語義,這與one是一致的。
引入const屬性
上面的四個表達式,我們只討論了左值和右值,我們再加上const進行討論。
所以:
string one("one"); 屬於非const左值 const string two("two"); const左值 string three() { return "three"; } 非const右值 const string four() { return "four"; } const右值
左值右值的屬性與const是正交的。
現在引入一個問題,如果有時候需要區分四種變量,那麼該使用什麼方法?
前面的討論,我們知道X&可以用來區分one,但是剩下的三個都可以被const X&吞掉,顯然我們需要爲一些變量提供一些定製版本的參數,來讓不同的變量優先選擇不同的參數。
C++11提供了右值引用來專門區分右值,我們同時提供四個函數:
void test(const string &s) { cout << "test(const string &s):" << s << endl; } void test(string &s) { cout << "test(string &s):" << s << endl; } void test(string &&s) { cout << "test(string &&s):" << s << endl; } void test(const string &&s) { cout << "test(const string &&s):" << s << endl; }
我們使用C++11進行編譯,發現:
test(string &s):one test(const string &s):two test(string &&s):three test(const string &&s):four
我們得出最佳匹配:
X & 匹配 非const左值
const X& 匹配 const左值
X && 匹配 非const右值
const X && 匹配 const右值
然後,我們可以採用逐個函數進行測試,發現:
X& 僅僅匹配 非const左值,這與之前的結論一致
const X& 可以匹配所有的表達式
X && 只可以匹配 非const右值
const X &&可以匹配const和非const 右值
OK,我們的問題解決,當我們需要區分右值的時候,就可以使用右值引用。
事實上,我們一般不需要const X &&,所以我們使用下列三種參數:
void test(string &s); 修改語義
void test(string &&s); 移動語義,後文介紹
void test(const string &s); 常量語義
這三種語義已經足夠,在C++98中我們只使用修改和常量語義,在C++11中,正是爲了實現移動語義,我們才需要區分右值,才需要引入右值引用。