2013-01-12 47 views
9

Tôi đang cố gắng để tìm hiểu thêm về tài liệu tham khảo rvalue nhưng tôi đã bị mắc kẹt trên ví dụ này đơn giản nhất:rvalue hello world thiếu constructor

#include <iostream> 
using namespace std; 

struct C { 
    C() { cout << "C()\n"; } 
    ~C() { cout << "~C()\n"; } 
    C(const C&) { cout << "C(const C&)\n"; } 
    C& operator=(const C&) { cout << "operator=(const C&)\n"; return *this; } 

    C(C&&) { cout << "C(C&&)\n"; } 
    C& operator=(C&&) { cout << "operator=(C&&)\n"; return *this; } 
}; 

C foo() { C c; return c; } 

int main() 
{ 
    const C c = foo(); 
    return 0; 
} 

tôi đã biên soạn nó với Clang 3.2 và -std=c++11 -fno-elide-constructors (để tránh (N) RVO) nhưng kết quả gây ngạc nhiên cho tôi:

C() 
~C() // huh? 
C(C&&) 
~C() 
~C() 

Tôi mong đợi chính xác là ngoại trừ ~C() đầu tiên. Nó đến từ đâu và tôi đang mất tích vì có 2 công trình xây dựng và 3 lần phá hủy? Trình xây dựng & & có được gọi là tham chiếu đối tượng bị hủy không ??

+0

Bước qua nó trong một trình gỡ lỗi và nhìn thấy nơi các cuộc gọi đến từ đó. Bạn nói đúng rằng số lượng các cuộc gọi hàm tạo và số lượng các cuộc gọi destructor sẽ giống nhau. –

+0

Tôi nhận được http://liveworkspace.org/code/IlzNk$0 đó là những gì được mong đợi –

+2

@SethCarnegie: nhưng bạn đã chọn GCC 4.7.2 phải không? Tôi đã cố gắng chọn clang 3.2 và tôi nhận được kết quả được hiển thị bởi OP –

Trả lời

3

Đây phải là lỗi. Các destructor cho đối tượng địa phương được xây dựng trong foo() đang được gọi trước khi di chuyển constructor của đối tượng nhận. Đặc biệt, có vẻ như tạm thời được cấp phát nhưng không được di chuyển() được xây dựng khi trở về theo giá trị. Các chương trình sau đây cho thấy điều này:

#include <iostream> 
using namespace std; 

struct C 
{ 
    C(int z) { id = z; cout << "C():" << id << endl; } 
    ~C() { cout << "~C():" << id << endl; } 
    C(const C& c) { id = c.id + 1; cout << "C(const C&):" << id << endl; } 
    C& operator=(const C&) { cout << "operator=(const C&)\n"; return *this; } 
    C(C&& c) { id = c.id + 1; cout << "C(C&&):" << id << endl;} 
    C& operator=(C&&) { cout << "operator=(C&&)\n"; return *this; } 
    int id; 
}; 

C foo() { C c(10); return c; } 

int main() 
{ 
    const C c = foo(); 
    return 0; 
} 

Output:

C():10 
// THE TEMPORARY OBJECT IS PROBABLY ALLOCATED BUT *NOT CONSTRUCTED* HERE... 
~C():10 // DESTRUCTOR CALLED BEFORE ANY OTHER OBJECT IS CONSTRUCTED! 
C(C&&):4198993 
~C():4198992 
~C():4198993 

Tạo hai đối tượng bên trong foo() dường như làm sáng tỏ thêm về vấn đề này:

C foo() { C c(10); C d(14); return c; } 

Output:

C():10 
C():14 
~C():14 
// HERE, THE CONSTRUCTOR OF THE TEMPORARY SHOULD BE INVOKED! 
~C():10 
C(C&&):1 // THE OBJECT IN main() IS CONSTRUCTED FROM A NON-CONSTRUCTED TEMPORARY 
~C():0 // THE NON-CONSTRUCTED TEMPORARY IS BEING DESTROYED HERE 
~C():1 

Điều thú vị là điều này dường như phụ thuộc vào cách đối tượng được xây dựng trong foo(). Nếu foo() được viết theo cách này:

C foo() { C c(10); return c; } 

Sau đó, lỗi xuất hiện. Nếu nó được viết theo cách này nó không:

C foo() { return C(10); } 

Output với định nghĩa mới nhất này của foo():

C():10 // CONSTRUCTION OF LOCAL OBJECT 
C(C&&):11 // CONSTRUCTION OF TEMPORARY 
~C():10 // DESTRUCTION OF LOCAL OBJECT 
C(C&&):12 // CONSTRUCTION OF RECEIVING OBJECT 
~C():11 // DESTRUCTION OF TEMPORARY 
~C():12 // DESTRUCTION OF RECEIVING OBJECT 
+0

Điều kỳ lạ là công việc này như mong đợi: C foo() {return C (10) ; }. Có vẻ như một lỗi đường dẫn mã rất cụ thể. Tôi sẽ chấp nhận câu trả lời này sau đó. – chrisaverage

+0

@chrisaverage: tôi nghĩ rằng vấn đề khá chung chung. Nếu 'foo()' được viết theo cách này: 'C foo() {C c (10); trở lại c; } 'sau đó nó đưa ra lỗi. Chỉ khi nó được viết theo cách này, nó không: 'C foo() {return C (10); } ' –

+0

Điều tôi ngụ ý là nó chỉ xảy ra khi đối tượng là một ứng cử viên cho NRVO nhưng không phải là RVO. Báo cáo lỗi được Michael đề cập trong các ý kiến ​​ban đầu dường như đã xác nhận điều này. – chrisaverage