2013-06-21 84 views
5

雖然問this question,瞭解到常量引用的臨時對象是在C時有效++破參考refnop指的是一個被破壞的臨時對象。我想知道爲什麼?常量引用臨時對象變爲後功能範圍(壽命)

#include <string> 
#include <map> 

struct A 
{ 
    // data 
    std::map <std::string, std::string> m; 
    // functions 
    const A& nothing()   const { return *this; } 
    void init()      { m["aa"] = "bb"; } 
    bool operator!= (A const& a) const { return a.m != m; } 
}; 

int main() 
{ 
    A a; 
    a.init(); 

    A const& ref = A(a); 
    A const& refnop = A(a).nothing(); 

    int ret = 0; 
    if (a != ref)  ret += 2; 
    if (a != refnop) ret += 4; 

    return ret; 
} 

使用GCC 4.1.2和MSVC 2010測試,它返回4;

$> g++ -g refnop.cpp 
$> ./a.out ; echo $? 
4 

refrefnop之間的區別是調用nothing()這確實沒什麼好說的。在這次調用之後,臨時對象被破壞!

我的問題:
爲什麼在refnop的情況下,臨時對象的生命週期是不一樣的const引用?

+0

注意:使用克++版本4.4和4.6,這段代碼返回0 ... – olibre

回答

9

臨時對象的生命週期擴展可以僅執行一次,當臨時對象被綁定到所述第一參考。在那之後,引用引用臨時對象的知識就消失了,所以進一步的生命週期擴展是不可能的。

是令人費解的,你

A const& refnop = A(a).nothing(); 

的情況與此類似的情況:

A const& foo(A const& bar) 
{ 
    return bar; 
} 
//... 
A const& broken = foo(A()); 

在這兩種情況下,暫時被綁定到函數參數(用於nothing()隱含thisbarfoo()),並得到它的壽命「擴展」到函數的參數的壽命。我把引號「擴展」,因爲臨時的自然壽命已經較長,所以沒有實際的擴展發生。

由於生命週期擴展屬性是非傳遞性的,因此返回引用(恰好引用臨時對象)不會進一步延長臨時對象的生命週期,結果refnopbroken最終引用不再存在的對象。

1

我原來的例子是複雜的。

所以我在這裏發佈一個簡單的例子,我提供相應的ISO C++ standard段落。

這個簡單的例子也可在coliru.stacked-crooked.com/

#include <iostream> 

struct A 
{ 
    A(int i) { std::cout<<"Cstr "<< i<<'\n'; p = new int(i); } 
~A()  { std::cout<<"Dstr "<<*p<<'\n'; delete p;  } 

    const A& thiz() const { return *this; } 

    int *p; 
}; 

const A& constref(const A& a) 
{ 
    return a; 
} 

int main() 
{ 
    const A& a4 = A(4); 
    const A& a5 = A(5).thiz(); 
    const A& a6 = constref(A(6)); 

    std::cout << "a4 = "<< *a4.p <<'\n'; 
    std::cout << "a5 = "<< *a5.p <<'\n'; 
    std::cout << "a6 = "<< *a6.p <<'\n'; 
} 
使用命令行 g++-4.8 -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

輸出:

Cstr 4 
Cstr 5 
Dstr 5 
Cstr 6 
Dstr 6 
a4 = 4 
a5 = 0 
a6 = 0 
Dstr 4 

正如你所看到的,通過a5a6引用的臨時對象的析構的分別的功能thizconstref端。

這是§12.2臨時對象,其中粗體部分適用於這種情況的提取物:

的第二上下文是當引用綁定到一個暫時的。 臨時到該參考結合或臨時 即一個子對象的完整的對象到所述基準 綁定持續基準的除了壽命:

  • 臨時結合到基準構件在構造函數的 ctor-initializer(12.6.2)中一直存在,直到構造函數退出。
  • 臨時綁定到函數調用(5.2.2) 中的引用參數,直到完成包含該調用的完整表達式。
  • 函數返回語句(6.6.3)中返回值 的臨時綁定的生存期未被擴展;臨時 在return語句中的完整表達式的末尾被銷燬。
  • 臨時結合到在新初始化(5.3.4)的參考持續 直到含有新初始化全表達的完成。

這是一個更完整的示例:

#include <iostream> 

struct A 
{ 
    A()   { std::cout<<"Cstr 9\n";   p = new int(v = 9);  } 
    A(int i) { std::cout<<"Cstr "<<i<<'\n'; p = new int(v = i);  } 
    A(const A&o){ std::cout<<"Copy "<<o.v<<'\n'; p = new int(v = 10+o.v); } 
    ~A()   { std::cout<<"Del "<<v<<' '<<*p<<'\n'; *p = 88; delete p; } 

    const A& thiz() const { return *this; } 

    int *p; 
    int v; 
}; 

const A& constref(const A& a) 
{ 
    return a; 
} 

std::ostream& operator<<(std::ostream& os, const A& a) 
{ 
    os <<"{ *p="<< *a.p <<" , v="<< a.v <<" }\n"; 
    return os; 
} 

int main() 
{ 
    std::cout << "---const A a1 = A(1)"    "\n"; 
        const A a1 = A(1); 
    std::cout << "---const A a2 = A(2).thiz()"   "\n"; 
        const A a2 = A(2).thiz(); 
    std::cout << "---const A a3 = constref(A(3))" "\n"; 
        const A a3 = constref(A(3)); 
    std::cout << "---const A& a4 = A(4)"    "\n"; 
        const A& a4 = A(4); 
    std::cout << "---const A& a5 = A(5).thiz()"   "\n"; 
        const A& a5 = A(5).thiz(); 
    std::cout << "---const A& a6 = constref(A(6))" "\n"; 
        const A& a6 = constref(A(6)); 

    std::cout << "a1 = "<< a1; 
    std::cout << "a2 = "<< a2; 
    std::cout << "a3 = "<< a3; 
    std::cout << "a4 = "<< a4; 
    std::cout << "a5 = "<< a5; 
    std::cout << "a6 = "<< a6; 
} 

和使用g++命令行相應的輸出:

---const A a1 = A(1) 
Cstr 1 
---const A a2 = A(2).thiz() 
Cstr 2 
Copy 2 
Del 2 2 
---const A a3 = constref(A(3)) 
Cstr 3 
Copy 3 
Del 3 3 
---const A& a4 = A(4) 
Cstr 4 
---const A& a5 = A(5).thiz() 
Cstr 5 
Del 5 5 
---const A& a6 = constref(A(6)) 
Cstr 6 
Del 6 6 
a1 = { *p=1 , v=1 } 
a2 = { *p=12 , v=12 } 
a3 = { *p=13 , v=13 } 
a4 = { *p=4 , v=4 } 
a5 = { *p=0 , v=5 } 
a6 = { *p=0 , v=6 } 
Del 4 4 
Del 13 13 
Del 12 12 
Del 1 1