[转载]非常量引用的初始值必须为左值的问题
首先,先看下面一個例子:
eg.1
#include<</span>iostream>
usingnamespace?std;
int?main(){
int?i?=2;
double?&r?=i;
return?0;
}
gcc?error:?invalid?initialization?of?reference?of?type?'double&'?from?expression?of?type?'int'
如果改成?const?double?&r?=i;沒有問題。
難道這里的i不是左值?
程序改成?:
eg.2
int?main(){
double?i?=2;
double?&r?=i;
return?0;
}
沒有錯誤
難道這里的i又是左值啦?
?
其實:
const?double?&r?=?i;?
由于類型不匹配,實際相當于:
const?double?inner_tmp?=?(double)i;??//這里就產生了一個臨時變量
const?double?&r?=?inner_tmp;
臨時的中間變量都是const,所有沒有const的引用會失敗。
?
左值與右值的區分
?
首先理解什么是右值引用(下面是百度百科給出的解釋):
???????右值引用(及其支持的Move語意和完美轉發)是C++0x將要加入的最重大語言特性之一,這點從該特性的提案在C++?-?State?of?the?Evolution列表上高居榜首也可以看得出來。從實踐角度講,它能夠完美解決C++中長久以來為人所詬病的臨時對象效率問題。從語言本身講,它健全了C++中的引用類型在左值右值方面的缺陷。從庫設計者的角度講,它給庫設計者又帶來了一把利器。從庫使用者的角度講,不動一兵一卒便可以獲得“免費的”效率提升…? 在標準C++語言中,臨時量(術語為右值,因其出現在賦值表達式的右邊)可以被傳給函數,但只能被接受為const?&類型。這樣函數便無法區分傳給const?&的是真實的右值還是常規變量。而且,由于類型為const?&,函數也無法改變所傳對象的值。C++0x將增加一種名為右值引用的新的引用類型,記作typename?&&。這種類型可以被接受為非const值,從而允許改變其值。
?
?區分左值與右值:
???????C++?11中引入的一個非常重要的概念就是右值引用。理解右值引用是學習“移動語義”(move?semantics)的基礎。而要理解右值引用,就必須先區分左值與右值。
???????對左值和右值的一個最常見的誤解是:等號左邊的就是左值,等號右邊的就是右值。
???????左值和右值都是針對表達式而言的,左值是指表達式結束后依然存在的持久對象,右值是指表達式結束時就不再存在的臨時對象。一個區分左值與右值的便捷方法是:看能不能對表達式取地址,如果能,則為左值,否則為右值。下面給出一些例子來進行說明。?
int?a?=?10;int?b?=?20;
int*?pFlag?=?&a;
vector<<span style="color: rgb(0, 0, 255); ">int>?vctTemp;
vctTemp.push_back(1);
string?str1?=?"hello?";
string?str2?=?"world";
const?int?&m?=?1;
請問,a,b,?a+b,?a++,?++a,?pFlag,?*pFlag,?vctTemp[0],?100,?string("hello"),?str1,?str1+str2,?m分別是左值還是右值?
什么是左值引用:
???????區分清楚了左值與右值,我們再來看看左值引用。左值引用根據其修飾符的不同,可以分為非常量左值引用(eg.1?double?&r?=i;)和常量左值引用(eg.1?const?double?&r?=i;)。
???????非常量左值引用只能綁定到非常量左值,不能綁定到常量左值、非常量右值和常量右值。如果允許綁定到常量左值和常量右值,則非常量左值引用可以用于修改常量左值和常量右值,這明顯違反了其常量的含義(eg.1中就是出現了非常量左值引用綁定到常量右值的情況)。如果允許綁定到非常量右值,則會導致非常危險的情況出現,因為非常量右值是一個臨時對象,非常量左值引用可能會使用一個已經被銷毀了的臨時對象。
???????常量左值引用可以綁定到所有類型的值,包括非常量左值、常量左值、非常量右值和常量右值。
?
為什么要區分非常量右值引用:
???????可以看出,使用左值引用時,我們無法區分出綁定的是否是非常量右值的情況。那么,為什么要對非常量右值進行區分呢,區分出來了又有什么好處呢?這就牽涉到C++中一個著名的性能問題——拷貝臨時對象。考慮下面的代碼:?
vector<<span style="color: rgb(0, 0, 255); ">int>?GetAllScores(){
vector<<span style="color: rgb(0, 0, 255); ">int>?vctTemp;
vctTemp.push_back(90);
vctTemp.push_back(95);
returnvctTemp;
}
???????當使用vector?vctScore?=?GetAllScores()進行初始化時,實際上調用了三次構造函數。盡管有些編譯器可以采用RVO(Return?Value?Optimization)來進行優化,但優化工作只在某些特定條件下才能進行。可以看到,上面很普通的一個函數調用,由于存在臨時對象的拷貝,導致了額外的兩次拷貝構造函數和析構函數的開銷。當然,我們也可以修改函數的形式為void?GetAllScores(vector?&vctScore),但這并不一定就是我們需要的形式。另外,考慮下面字符串的連接操作:
strings1("hello");strings?=?s1?+?"a"+?"b"+?"c"+?"d"+?"e";
???????在對s進行初始化時,會產生大量的臨時對象,并涉及到大量字符串的拷貝操作,這顯然會影響程序的效率和性能。怎么解決這個問題呢?如果我們能確定某個值是一個非常量右值(或者是一個以后不會再使用的左值),則我們在進行臨時對象的拷貝時,可以不用拷貝實際的數據,而只是“竊取”指向實際數據的指針(類似于STL中的auto_ptr,會轉移所有權)。C++?11中引入的右值引用正好可用于標識一個非常量右值。C++?11中用&表示左值引用,用&&表示右值引用,如:
int&&a?=?10;? ???????右值引用根據其修飾符的不同,也可以分為非常量右值引用和常量右值引用。
???????非常量右值引用只能綁定到非常量右值,不能綁定到非常量左值、常量左值和常量右值(VS2010?beta版中可以綁定到非常量左值和常量左值,但正式版中為了安全起見,已不允許)。如果允許綁定到非常量左值,則可能會錯誤地竊取一個持久對象的數據,而這是非常危險的;如果允許綁定到常量左值和常量右值,則非常量右值引用可以用于修改常量左值和常量右值,這明顯違反了其常量的含義。
???????常量右值引用可以綁定到非常量右值和常量右值,不能綁定到非常量左值和常量左值(理由同上)。可以看出,使用左值引用時,我們無法區分出綁定的是否是非常量
轉載于:https://www.cnblogs.com/zhoug2020/p/7821125.html
總結
以上是生活随笔為你收集整理的[转载]非常量引用的初始值必须为左值的问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LeetCode 37 解数独
- 下一篇: Lucene搜索引擎例子demo