2013-09-23 88 views
5
#include<iostream> 
using namespace std; 

int &fun() 
{ 
    static int x = 10; 
    return x; 
} 
int main() 
{ 
    fun() = 30; 
    cout << fun(); 
    return 0; 
} 

函數fun()通過引用返回值,但在main()方法中我將一些int賦值給函數。理想情況下,編譯器應該顯示一個像左值所需的錯誤,但在上述情況下,程序可以正常工作。爲什麼這樣?爲函數返回引用賦值

+5

返回參考靜態變量是左值(和法律)。你爲什麼認爲應該有任何錯誤? –

+0

'fun()'返回的值是'int&',它是'lvalue' –

+0

評估引用*會導致*左值。 –

回答

7

這是鬆散和馬虎的語言說「一個函數返回的東西」。如果你知道如何處理這個問題,這可以作爲簡寫,但在這種情況下,你會感到困惑。

更正確的方法來考慮它是你評估函數調用表達式。這樣做會給你一個。值是一個右值或左值(模數細節)。

T是一個對象類型,並且您評估返回類型爲T的函數時,您會得到一個類型爲T的值,它是一個右值。另一方面,如果函數的返回類型爲T &,則會得到類型爲T的值,該值是一個左值(並且該值是與return語句中的引用綁定的值)。

+2

請注意,只有當f()計算爲*內建類型*的右值時,'f()= value'纔是錯誤的,否則如果它評估爲類類型的右值則是正確的。 (左值在任何情況下都已經很好) – Nawaz

3

返回一個引用是非常有用的。

例如它是std::map::operator[]所做的。我希望你喜歡寫my_map[key] = new_value;的可能性。

如果一個常規(非運算符)函數返回一個引用,那麼它可以分配給它,我沒有看到任何理由,這應該被禁止。

如果您真的想要,您可以通過返回const X&或返回X來防止分配。

2

它的工作原因是該功能的結果左值。參考文獻是左值。基本上,從函數返回一個非const引用的全部就是能夠賦值給它(或對引用對象執行其他修改)。

1

L值是一個定位器值。這意味着它有地址。一個參考清楚地有一個地址。該左值要求如果按值從樂趣()返回,你可以得到:

#include<iostream> 
using namespace std; 

int fun() 
{ 
    static int x = 10; 
    return x; 
} 
int main() 
{ 
    fun() = 30; 
    cout << fun(); 
    return 0; 
} 
2

除了其他的答案,考慮下面的代碼:

SomeClass& func() { ... } 

func().memberFunctionOfSomeClass(value); 

這是一個非常自然的事如果你期望編譯器給你一個這樣的錯誤,我會很驚訝。

現在,當您編寫some_obj = value;幕後真正發生的是您撥打some_obj.operator =(value);。而operator =()只是您班級的另一個成員功能,與memberFunctionOfSomeClass()沒有區別。

所有的一切,把它歸結爲:

func() = value; 
// equivalent to 
func().operator =(value); 
// equivalent to 
func().memberFunctionOfSomeClass(value); 

當然這是過於簡單,並且這個符號並不適用於內建類型,如int(但使用相同的機制)。

希望這會幫助你更好地理解其他人已經解釋過的左值

1

我也被類似的代碼所迷惑 - 在拳頭。這是「爲什麼我給函數調用賦值,以及爲什麼編譯器對它感到滿意?」我質疑自己。但是當你看到「後面」會發生什麼時,它確實有道理。


由於cpp和其他poined出來,左值是「存儲位置」有地址,我們可以給它們賦值。您可以在左值和右值on the internet的主題中找到更多信息。

當我們在看功能:

int& fun() 
{ 
    static int x = 10; 
    return x; 
} 

我感動&的類型,所以這是比較明顯的,我們正在返回的引用爲int。
我們看到我們有x,這是左值 - 它有地址,我們可以分配給它。它也是靜態的,這使得它非常特殊 - 如果它不是靜態的,變量的生命期(範圍)將在離開函數時以堆棧展開結束,然後參考可指向宇宙中存在的任何黑洞。然而,由於x是靜態的,即使在我們離開函數之後(以及當我們再次返回函數時)它也會存在,我們可以在函數之外訪問它。

我們正在返回一個int的引用,由於我們返回x,它是對x的引用。然後,我們可以使用該參考來更改函數外部的x。所以:

int main() 
{ 
    fun(); 

我們只是調用函數。變量x(在樂趣函數的範圍內)被創建,它的值爲10賦值。即使在功能被留下之後,它的地址和價值仍然存在 - 但我們無法使用它的價值,因爲我們沒有地址。

fun() = 30; 

我們調用的函數,然後改變X值。通過函數返回的引用更改值x注意:該函數被稱爲第一個,並且只有在函數調用完成後,然後,分配發生。

int& reference_to_x = fun(); // note the & 

現在我們(終於)保持由該函數返回的參考X。現在我們可以改變x而不先調用函數。(reference_to_x將可能有相同的地址X有樂趣函數內)

int copy_of_x = fun(); // no & this time 

這一次,我們創造新的int和我們剛纔複製的X值(通過參考)。這個新的int有它自己的地址,它不指向x like reference_to_x是。

reference_to_x = 5; 

我們分配X值5至基準,而我們甚至沒有調用的函數。 copy_of_x未更改。

copy_of_x = 15; 

我們換了新的int價值15 X沒有改變,因爲copy_of_x有它自己的地址。

} 


由於6502和其他人指出的那樣,我們使用返回引用了大量與容器和自定義的替換類似的方法。

std::map<std::string, std::string> map = {}; 

map["hello"] = "Ahoj"; 
// is equal to 
map.operator[]("hello") = "Ahoj"; // returns reference to std::string 
// could be done also this way 
std::string& reference_to_string_in_map = map.operator[]("hello"); 
reference_to_string_in_map = "Ahoj"; 

我們使用可以有聲明這樣的地圖功能:

std::string& map::operator[](const std::string& key); // returns reference 

我們沒有地址,我們「存儲」在地圖的字符串,所以我們稱之爲地圖此重寫功能,傳遞它以便map知道我們想要訪問哪個字符串,並且它返回給我們的那個字符串的引用,我們可以用它來改變這個值。 注意:該函數再次被調用,並且只有在完成後(地圖找到正確的字符串並返回對其的引用)纔會進行分配。這就像有樂趣()= 10,只有更beatiful ...

希望這有助於人誰仍然woudn't甚至閱讀其他的答案後明白了一切......