2017-05-06 106 views
2

爲什麼下面的代碼...引用匿名右值已損壞

#include <iostream> 
#include <map> 

template< typename T, typename U > 
class Map 
{ 
public: 
    Map(const T& t, const U& u) { map_[ t ] = u; } 
    Map< T, U >& operator() (const T& t, const U& u) 
    { 
    map_[ t ] = u; 
    return *this; 
    } 
    U& operator[] (const T& t) { return map_[ t ]; } 

private: 
    std::map< T, U > map_; 
}; 

Map< int, std::string >& g_map = Map< int, std::string> (1, "lorem") 
                 (3, "ipsum") 
                 (5, "dolor"); 

int main(int argc, char* argv[]) 
{ 
    std::cout << g_map[3] << std::endl; 
    return 0; 
} 

......產生這種損壞的輸出?...

>g++ -g main.cpp 
>./a.out 
ipsumÿÿÿÿlorem!h€Ap€AD€A!ˆ€A¼gì¿P€A€A,€A!p€A€AY 

我瞭解到最近,分配給一個參考一個匿名的右值擴展了右值對象的生命週期。所以我認爲由於匿名權值std::map被全局範圍g_map引用,所以它的生命週期將被擴展到全局範圍變量的範圍,並且將g_map作爲任何其他全局變量(如果不是作爲參考,匿名右值將在結束分號處死亡)。

有人可以解釋一下終生延長規則是如何適用於上述的嗎?

使用gcc編譯4.9.2。

+0

[Works for me](http://rextester.com/HTWI33012),它的價值。 –

+0

啊,我知道發生了什麼事。你的程序表現出未定義的行爲:'g_map'是一個懸而未決的參考。 –

+0

@StoneThrow MS VS當然,只有該編譯器允許臨時隱式轉換爲左值 – Slava

回答

4

你基本上是這樣:

class C { 
public: 
    C& detemporize() { return *this; } 
}; 

C& cr = C().detemporize(); 

創建臨時C實例。然後調用一個方法,該方法返回C&引用。編譯器不知道也不關心返回值是指同一個臨時值;儘管它知道,但它很可能會返回對某個全球性,長期存在的對象的引用。

無論如何,cr最後指的是臨時的,然後立即死亡,留下cr懸空。任何後續嘗試使用它都會顯示未定義的行爲。

在您的代碼中,Map::operator()扮演的角色是detemporize(),而g_map仍然是一個懸掛引用。

+0

你說的話是有道理的,但最近我被告知給臨時對象分配一個引用「延長了它的壽命」。現在我不清楚這是否屬實,如果這種延伸是多久的話。如果存在延長壽命這樣的事情,我會希望在我/你的例子中臨時性地「成爲全球範圍」,因爲全局變量沒有「關閉大括號」,所以我推斷變量的壽命將會是「提升」到與真正的全球變量相同(即在程序退出時死亡)。 – StoneThrow

+0

雖然你並沒有把一個臨時的參考文件綁定到一個參考文獻上,但你正在約束一個左值。 'const C&cr = C();'會以你描述的方式工作,延長'C()'創建的引用的生命週期。 –

+0

我有點不清楚爲什麼在我的例子中,你說我將_lvalue_綁定到引用。我的詞彙可能是原始的,但我認爲左值是「等號左邊的一個變量」,我在這幅圖中看不到這個對象。 – StoneThrow

2

我最近了解到,分配對匿名右值的引用擴展了右值對象的生存期。

這僅發生在你直接臨時對象賦值給一個參考:

const obj &ref1 = obj(); // extends 
const obj &ref = somefuncthatreturnsobj(); // extends 

但沒有魔法存在,如果您調用隱藏了參考不知它不工作了一個功能:

class foo { 
    const foo &get() const { return *this; }; 
}; 

const foo &ref1 = foo(); // extends lifetime of temporary 
const foo &ref2 = foo().get(); // no lifetime extention, dangling reference 
+0

C++標準中有沒有解釋/證明這種行爲的東西?我覺得在臨時調用一個函數還是另一個暫時的...不是? – StoneThrow

+0

@StoneThrow如果對錶達式類別使用正確的術語,則會更加清楚......當引用僅與* prvalue *綁定時,會出現生命週期擴展。對返回左值引用的函數的函數調用是*左值*。 –

+2

@StoneThrow考慮:'class Map {static longLivedInstance; Map&operator()(){return longLivedInstance; }}; Map&ref = Map()();'在這裏,調用一個臨時函數返回一個對那個暫時以外的東西的引用;而方法的簽名與'return * this;'完全相同。你基本上期望編譯器通過檢查函數的實際實現來猜測你的意思。這甚至沒有幫助 - 想象一下'返回rand()%2的方法嗎? longLivedInstance:* this;' –