2009-06-24 19 views
14

當我編譯使用g++引用類型轉換操作符:請問麻煩?

class A {}; 

void foo(A&) {} 

int main() 
{ 
    foo(A()); 
    return 0; 
} 

我收到以下錯誤消息下面的代碼:

> g++ test.cpp -o test  
test.cpp: In function ‘int main()’: 
test.cpp:10: error: invalid initialization of non-const reference of type ‘A&’ from a temporary of type ‘A’ 
test.cpp:6: error: in passing argument 1 of ‘void foo(A&)’ 

一些反映後,這些錯誤使大量的意義對我來說。 A()只是一個臨時值,而不是棧上的可分配位置,所以它似乎沒有地址。如果它沒有地址,那我就不能參考它。好的。

但是等等!如果我將以下轉換運算符添加到類A

class A 
{ 
public: 
    operator A&() { return *this; } 
}; 

那麼一切都很好!我的問題是,這是否是遠程安全的。當A()被構建爲臨時值時,this指向什麼?

我給出的事實,

void foo(const A&) {} 

可以根據g++,我已經使用的所有其他編譯器接受臨時值一些信心。 const關鍵字總是可以丟棄,所以如果在const A&參數和A&參數之間存在任何實際的語義差異,我會感到驚訝。所以我想這是另一種方式來問我的問題:爲什麼const引用編譯器認爲安全的臨時值,而非const引用不是?

回答

16

這不是一個地址不能被採取(編譯器總是可以命令它在堆棧上,它與ref-to-const),這是一個程序員的意圖問題。使用接口A &,它說「我將修改此參數中的內容,以便您可以在函數調用後讀取」。如果你臨時傳遞它,那麼它「修改」的東西在函數後不存在。這可能(可能)是編程錯誤,因此不允許。例如,考慮:

void plus_one(int & x) { ++x; } 

int main() { 
    int x = 2; 
    float f = 10.0; 

    plus_one(x); plus_one(f); 

    cout << x << endl << f << endl; 
} 

這並不編譯,但如果可以的臨時綁定到一個引用到非const,它會編譯,但有驚人的結果。在plus_one(f)中,f將被隱式轉換爲臨時int,plus_one將接受temp並將其遞增,從而保留底層float f不變。當plus_one返回時,它不起作用。這幾乎肯定不是程序員的意圖。


這條規則偶爾會搞砸。一個常見的例子(描述here)試圖打開一個文件,打印一些東西並關閉它。你會希望能夠做到:

ofstream("bar.t") << "flah"; 

但你不能因爲運營商< <需要裁判對非const。您的選項掰成兩行,或調用方法返回一個引用到非const:

ofstream("bar.t").flush() << "flah"; 
+0

我同意這種事通常是程序員錯誤。如果你好奇,我試圖傳遞的類是一種智能指針,它有一個基礎的堆分配指針成員。實際上,我希望接收函數能夠使用底層指針進行操作。我只想確保臨時智能指針不會被銷燬(遞減引用計數),直到接收函數返回。 – Ben 2009-06-24 21:23:09

4

當你給一個const引用賦一個r值時,你保證在引用被銷燬之前臨時文件不會被銷燬。當你分配給一個非const引用時,沒有這樣的保證。

int main() 
{ 
    const A& a2= A(); // this is fine, and the temporary will last until the end of the current scope. 
    A& a1 = A(); // You can't do this. 
} 

你不能安全地拋棄常量並期望事情能夠正常工作。 const和非const引用有不同的語義。

+0

通常,在評估創建它的表達式的末尾臨時被破壞。當一個臨時的生命期延長時,有兩個例外,其中一個由@Eclipse指出。請參閱:http://en.cppreference.com/w/cpp/language/lifetime#Temporary_object_lifetime – winnetou 2015-12-03 15:36:41

3

,有些人可能碰上疑難雜症:MSVC的編譯器(Visual Studio的編譯器,驗證與Visual Studio 2008)編譯這個代碼沒有問題。我們一直在一個項目中使用這個範例,通常需要一個參數(一個數據塊來消化),但有時想要搜索塊並將結果返回給調用者。另一種模式是通過接收三個參數來啓用的---第二個參數是要搜索的信息(缺省參考爲空字符串),第三個參數是返回數據(缺省參考到所需類型的空列表)。

這個範例在Visual Studio 2005和2008中工作,我們不得不重構它,以便列表被構建並返回,而不是由調用者和擁有者所擁有並用g ++編譯。

如果有一種方法可以將編譯器開關設置爲禁止MSVC中的這種行爲或在g ++中允許它,我會很高興知道; MSVC編譯器的寬容性/ g ++編譯器的限制增加了移植代碼的複雜性。