2013-06-21 29 views
5

Trong khi hỏi this question, tôi đã học tham chiếu const đến một đối tượng tạm thời là hợp lệ trong C++:const tham chiếu đến một đối tượng tạm thời bị phá vỡ sau khi phạm vi chức năng (thời gian sống)

int main() 
{ 
    int a = 21; 
    int b = 21; 

    //error: invalid initialization of non-const reference 
    //int  & sum = a + b;e [...] 

    //OK 
    int const & sum = a + b; 

    return sum; 
} 

Nhưng trong ví dụ sau, const tham chiếu refnop dùng để chỉ đối tượng tạm thời bị hủy. Tôi tự hỏi tại sao?

#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; 
} 

Được thử nghiệm bằng GCC 4.1.2 và MSVC 2010, trả về 4;

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

Sự khác biệt giữa refrefnop là cuộc gọi đến nothing() mà không thực sự không có gì. Có vẻ như sau cuộc gọi này, vật thể tạm thời bị phá hủy!

Câu hỏi của tôi:
Tại sao trong trường hợp refnop, thời gian sống của đối tượng tạm thời không giống như tham chiếu const?

+0

Chú ý: sử dụng g ++ phiên bản 4.4 và 4.6, đoạn mã này trả về 0 ... – olibre

Trả lời

9

Phần mở rộng suốt đời của một đối tượng tạm thời chỉ có thể được thực hiện một lần, khi đối tượng tạm thời bị ràng buộc với tham chiếu đầu tiên. Sau đó, kiến ​​thức rằng tham chiếu đề cập đến một đối tượng tạm thời đã biến mất, do đó, không thể mở rộng thêm vòng đời.

Các trường hợp đó được đánh đố bạn

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

cũng tương tự như trường hợp này:

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

Trong cả hai trường hợp, tạm thời bị ràng buộc với lập luận chức năng (implicit this cho nothing(), bar cho foo()) và kéo dài tuổi thọ của nó 'kéo dài' cho toàn bộ thời gian của đối số hàm. Tôi đặt 'mở rộng' trong dấu ngoặc kép, bởi vì thời gian sống tự nhiên của tạm thời đã lâu hơn, do đó không có phần mở rộng thực sự diễn ra.

Vì thuộc tính mở rộng suốt đời không chuyển đổi, trả về tham chiếu (xảy ra để chỉ một đối tượng tạm thời) sẽ không kéo dài thời gian tồn tại của đối tượng tạm thời, kết quả là cả hai tham số refnopbroken các đối tượng không còn tồn tại.

1

Ví dụ ban đầu của tôi phức tạp.

Vì vậy, tôi đăng ở đây một ví dụ đơn giản hơn và tôi cung cấp đoạn ISO C++ standard tương ứng.

dụ đơn giản này cũng có sẵn trên 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'; 
} 

Sản lượng sử dụng dòng lệnh 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 

Như bạn có thể thấy, các đối tượng tạm thời được tham chiếu bởi a5a6 được destructed tại kết thúc các hàm thizconstref tương ứng.

Đây là một trích §12.2 đối tượng tạm thời, nơi phần in đậm áp dụng trong trường hợp này:

Bối cảnh thứ hai là khi một tài liệu tham khảo được ràng buộc với một tạm thời. Các tạm thời để mà tham chiếu được đóng thành quyển hoặc tạm thời đó là đối tượng hoàn toàn của một subobject để mà tham chiếu là ràng buộc kéo dài tuổi thọ của các tài liệu tham khảo trừ:

  • Một tạm thời bị ràng buộc cho một thành viên tham khảo trong bộ dựng khởi tạo ctor-initializer (12.6.2) của nó cho đến khi hàm khởi tạo xuất hiện.
  • Giới hạn tạm thời đối với tham số tham chiếu trong cuộc gọi hàm (5.2.2) vẫn tiếp tục cho đến khi hoàn thành biểu thức đầy đủ chứa cuộc gọi.
  • Thời gian tồn tại của một giới hạn tạm thời với giá trị trả lại trong một câu lệnh trả về hàm (6.6.3) không được mở rộng; tạm thời bị hủy ở cuối biểu thức đầy đủ trong câu lệnh trả về.
  • Một tạm thời bị ràng buộc vào một tài liệu tham khảo trong một mới initializer (5.3.4) vẫn tồn tại cho đến khi kết thúc việc thể hiện đầy đủ chứa mới initializer.

Đây là một ví dụ hoàn chỉnh hơn:

#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; 
} 

Và đầu ra tương ứng sử dụng cùng một dòng g++ lệnh:

---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