2013-02-20 61 views
3
class C 
{ 
public: 
    int True(int i) const 
    { 
     return i+2; 
    } 
}; 


const C& F(const C& c) 
{ 
    return c; 
} 

int main() 
{ 
    const C& c = F(C());        // Line 1 
    cout << boolalpha << c.True(1) << endl;   // Line 2 
} 

問題>爲什麼上面的代碼可以打印正確的值? 我假設變量c將涉及到一個無效的臨時C對象,當它擊中線2臨時對象被破壞後爲什麼不崩潰

//更新

我想爲什麼我對這個問題的關心更新此OP來說明原因。

這裏是C++模板代碼片段:完全指南

// maximum of two values of any type 
template <typename T> 
inline T const& max (T const& a, T const& b) 
{ 
    return a < b ? b : a; 
} 

正如你可以看到函數返回參照傳遞參數。 我只是想知道爲什麼沒有以下的版本,而不是:

// maximum of two values of any type 
template <typename T> 
inline T max (T const& a, T const& b) 
{ 
    return a < b ? b : a; 
} 
+2

未定義的行爲? – juanchopanza 2013-02-20 20:23:35

+0

@juanchopanza,'const&'不讓它活着嗎? – hmjd 2013-02-20 20:24:13

+3

@hmjd:臨時存在直到它們被創建的完整表達式的末尾,在這種情況下是'F(C())';在'F'返回後,對象已經死亡。 'const C&c = C();',另一方面,它的生命期會延長。 – ildjarn 2013-02-20 20:25:33

回答

3

通常情況下,當一個臨時綁定到const引用,臨時的壽命延長到基準的壽命。因此,如果您的代碼說const C& c = C()那麼臨時會生活只要c

但是,您正在將臨時設備傳遞給另一個功能F()。在這種情況下,C++ 11規範的第12.2.5節規定臨時將持續到包含該調用的完整表達式的完成。

因此,當您說const C& c = F(C())時,臨時C()實際上在本聲明結束時被破壞,並且在下一行中不再有效。

也就是說,你的代碼似乎正常工作,因爲編譯時已知調用c.True(),並且函數定義實際上並不涉及c中的任何數據,所以暫時死亡的事實並不是'真的會影響觀察到的行爲。然而這在技術上是未定義的行爲。

+0

只有當它被綁定到一個const引用。 (請參閱http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/) – mirk 2013-02-20 20:32:47

5

的第12.2/4的C++ 11標準規定在某些情況下的臨時的壽命的確可以延伸超出在產生它們的充分表達的端:

有是兩個上下文其中臨時被銷燬在不同於完整表達式結束的點。 [...]

第一個上下文不相關。但是,根據第12.2/5段:

第二個上下文是引用綁定到臨時的時候。 到的引用綁定或者是其中引用綁定一個子對象的完整的對象持續基準的壽命除了臨時臨時:

- 甲暫時結合到一個參考成員在構造函數的ctor-initializer(12.6.2)中一直存在,直到構造函數退出。

- 臨時綁定到函數調用(5.2.2)中的引用參數,直到完成包含調用的完整表達式。

- 在函數return語句(6.6.3)中臨時綁定到返回值的生命週期不會被擴展;在return語句的完整表達式的末尾臨時被銷燬。

- 臨時綁定到新初始化程序(5.3.4)中的引用,直到完成包含新初始化程序的完整表達式。

這裏,C()構建的臨時勢必的功能F參數c。因此,在包含函數F()的調用的完整表達式的末尾銷燬該臨時表,並且返回的引用是懸掛的

調用函數True()就會導致未定義的行爲

2

這會調用未定義的行爲。一旦涉及F(C())的完整表達式完成,由C()創建的臨時文件將被銷燬,因此F返回的引用不再有效。

但是,未定義的行爲並不能保證你的程序崩潰。在更煩人的情況下(像這樣),它只會導致微妙的,很難診斷錯誤。至於爲什麼這個未定義的行爲給你這個特定的結果,我把你引用到這個famous answer

+2

技術上不正確。只有在引用被分配給'c'後,纔會銷燬由'C()'創建的臨時文件。在完整表達式的末尾。 – 2013-02-20 20:40:03

+0

@凱文謝謝,我的標準並不像應該那麼好。它是否正確? – 2013-02-20 20:41:28

+0

由於您現在正在引用完整表達式,因此確定。 – 2013-02-20 20:50:41

1

你看到的是未定義的行爲。這很可能是因爲你調用的函數根本不依賴於對象的狀態或vtable,編譯器將它內聯到cout << boolalpha << (1+2);,所以不管對象是否被銷燬都沒有關係 - 事實上,編譯器可能會甚至沒有打擾創建它。

例如,在VS2010中,在「調試」中,它將FTrue稱爲靜態調用。由於True沒有引用this它的代碼恰好工作正常。 (它甚至可能仍然可以工作,因爲C中沒有成員變量可以訪問,所以它唯一可以做的就是打印出this的地址,這只是堆棧中的一個地址。如果C具有僅有C改變成員變量的析構函數和True使用它們,那麼你會看到一個差異 - 在所有情況下,該行爲是不確定的,只是執​​行的假象)

在‘釋放’VS2010不打擾任何C對象或調用FTrue - 它只是調用cout << boolalpha << 3,在編譯時確定了值C::True(2)。在編譯器生成的程序中沒有臨時對象,無效或無效。

所以,僅僅因爲在一個對象上調用一個函數似乎有效,它並不意味着該對象在編譯器生成的程序中存在或曾經存在過。具有不同未定義行爲的不同源程序可能會導致編譯器生成可執行文件,從而引發訪問衝突或出現其他一些行爲。


結合一個返回值const引用僅適用於由價值迴歸,而不是返回到參數或局部的引用,否則編譯器將需要解決停機問題,以確定對象的生命週期。

例如,這樣的代碼:

#include<iostream> 

class C 
{ 
public: 
    int t; 
    C(int t) : t(t){} 
    ~C() { std::cout << __FUNCTION__ << " " << t << std::endl; } 
}; 

const C& F(const C& c) 
{ 
    return c; 
} 
const C& G() 
{ 
    return C(2); 
} 
C H() 
{ 
    return C(3); 
} 

int main() 
{ 
    const C& c = F(C(1)); 
    std::cout << "main 1" << std::endl; 
    const C& d = G();  
    std::cout << "main 2" << std::endl; 
    const C& e = H();  
    std::cout << "main 3" << std::endl; 
} 

結果在該輸出 - 僅由H()值返回,所以只有第三C已其生命週期延長:

C::~C 1 
main 1 
C::~C 2 
main 2 
main 3 
C::~C 3