2009-05-28 2 views
10
class A 
{ 
public: 
    A(const int n_); 
    A(const A& that_); 
    A& operator=(const A& that_); 
}; 

A::A(const int n_) 
{ cout << "A::A(int), n_=" << n_ << endl; } 

A::A(const A& that_) // This is line 21 
{ cout << "A::A(const A&)" << endl; } 

A& A::operator=(const A& that_) 
{ cout << "A::operator=(const A&)" << endl; } 

int foo(const A& a_) 
{ return 20; } 

int main() 
{ 
    A a(foo(A(10))); // This is line 38 
    return 0; 
} 

Thực thi mã này cho o/p:Tại sao ctor sao chép được sử dụng trong mã này?

A::A(int), n_=10
A::A(int), n_=20

Rõ ràng các nhà xây dựng bản sao không bao giờ được gọi.

class A 
{ 
public: 
    A(const int n_); 
    A& operator=(const A& that_); 
private: 
    A(const A& that_); 
}; 

Tuy nhiên, nếu chúng ta làm cho nó tin, biên dịch lỗi này xảy ra:

Test.cpp: In function ‘int main()’:
Test.cpp:21: error: ‘A::A(const A&)’ is private
Test.cpp:38: error: within this context

Tại sao biên dịch phàn nàn khi nó không thực sự sử dụng constructor sao chép?
Tôi đang sử dụng phiên bản gcc 4.1.2 20070925 (Mũ Đỏ 4.1.2-33)

Trả lời

12

Core defect 391 giải thích sự cố.

Về cơ bản, chuẩn C++ hiện tại yêu cầu một hàm tạo bản sao có sẵn khi chuyển một loại tạm thời của loại lớp thành tham chiếu const.

Yêu cầu này sẽ bị xóa trong C++ 0x.

Logic đằng sau đòi hỏi một constructor sao chép xuất phát từ trường hợp này:

C f(); 
const C& r = f(); // a copy is generated for r to refer to 
+0

FYI Tôi nghĩ rằng điều này vẫn còn hiện diện trong C++ 11 ('12.2/1'). –

1

Trình tạo bản sao không được sử dụng, nhưng để mã biên dịch trình tạo bản sao cần phải truy cập được.

EDIT: Comeau C++ biên dịch báo cáo như sau:

Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for ONLINE_EVALUATION_BETA2 
Copyright 1988-2008 Comeau Computing. All rights reserved. 
MODE:strict errors C++ noC++0x_extensions 

"ComeauTest.c", line 38: error: "A::A(const A &)" (declared at line 17), required 
      for copy that was eliminated, is inaccessible 
    A a(foo(A(10))); // This is line 38 
      ^

1 error detected in the compilation of "ComeauTest.c". 

Lưu ý rằng nếu C++ 0x phần mở rộng được kích hoạt, nó biên dịch tốt trong Comeau C++ biên dịch.

+0

Comeau cần phải sao chép đối tượng A tạm thời để làm gì? Nó được thông qua bằng cách tham khảo! – xtofl

+0

Và bạn đang đề cập đến Tối ưu hóa giá trị trả lại nào? – xtofl

+1

Không có đối tượng nào đang được trả về để RVO không liên quan đến việc sử dụng hàm tạo bản sao. A (10) là một rvalue của loại A, mặc dù, và trình biên dịch có thể chọn để có một bản sao của điều này để ràng buộc tham số tham chiếu đến. Đó là bản sao tiềm năng này yêu cầu các nhà xây dựng sao chép có thể truy cập được. –

3

Đến nay tôi thấy bạn không sử dụng hàm tạo bản sao ở bất kỳ đâu. Trong tuyên bố foo(A(10)) bạn đang tạo đối tượng tạm thời của lớp A và chuyển nó làm tham chiếu const cho foo. Hàm foo trả về một số nguyên được sử dụng trong việc xây dựng đối tượng a. Do đó tôi không thấy nơi mà các nhà xây dựng bản sao đang tham gia ở đây và làm thế nào NRVO đi vào hình ảnh. Ngoài ra, tôi biên dịch các mã sau đây bằng cách làm cho các nhà xây dựng bản sao tư nhân và nó biên dịch tốt trong VS2008.

using namespace std; 

class A 
{ 
public: 
    A(const int n_); 
private: 
    A(const A& that_); 
    A& operator=(const A& that_); 
}; 

A::A(const int n_) 
{ cout << "A::A(int), n_=" << n_ << endl; } 

A::A(const A& that_) // This is line 21 
{ cout << "A::A(const A&)" << endl; } 

A& A::operator=(const A& that_) 
{ 
    cout << "A::operator=(const A&)" << endl; 
    return *this; 
} 

int foo(const A& a_) 
{ return 20; } 


int main(int argc,char *argv[]) 
{ 
    A a(foo(A(10))); // This is line 38 
    return 0; 

} 
+0

Tương tự trên VS2005, nhân tiện. – xtofl

+1

@AngryWhenHungry: BTW, bạn đã bỏ lỡ trả lại * điều này trong toán tử gán. Tôi hy vọng rằng đó không phải là lỗi biên dịch bạn đã nhận được :-) – Naveen

+1

Kiểm tra câu trả lời của James Hopkin: http://stackoverflow.com/questions/919701/c-why-is-the-copy-ctor-used-in-this -code/920118 # 920118. Về cơ bản, ràng buộc một tham chiếu tạm thời với tham chiếu const * vẫn * yêu cầu ctor sao chép (trong C++ 03). –

0

Nói chung, bạn không nên lo lắng về việc khi nào và khi nào trình tạo bản sao được gọi. Tiêu chuẩn C++ khá thoải mái khi các cuộc gọi đến trình tạo bản sao sẽ bị xóa hoặc cho vấn đề đó được thêm vào. Nếu lớp của bạn một cách hợp lý cần nó, hãy cung cấp nó (và đừng quên toán tử hủy và gán) là quy tắc hợp lý.

5

Tiêu chuẩn năm 2003, trong §12.2/1, khẳng định:

Even when the creation of the temporary object is avoided (12.8), all the semantic restrictions must be respected as if the temporary object was created. [Example: even if the copy constructor is not called, all the semantic restrictions, such as accessibility (clause 11), shall be satisfied. ]

Có những ví dụ tương tự xung quanh. Từ những gì tôi thu thập, trình biên dịch được tự do tạo ra các thời gian tạm thời hoặc tối ưu hóa chúng.

+0

Nhưng ngữ nghĩa của foo (const A &) ra lệnh sử dụng một hàm tạo bản sao như thế nào? – xtofl

2

Chỉ một nhận xét khác: trình biên dịch thực hiện một việc khác khi làm việc với tạm thời. Vì vậy, nó không phải về các nhà xây dựng bản sao, đó là về tạm thời trung gian.

A original(10); 
foo(original); // does compile 
foo(A(10)); // doesn't compile - needs a copy constructor 
0

Khi gọi:

foo(A(10)); 

một đối tượng tạm thời được tạo ra trong suốt thời gian của cuộc gọi. Một hàm tạo bản sao đang được sử dụng để điền dữ liệu. Đối tượng tạm thời bị xóa sau khi thực hiện cuộc gọi.

Khi gọi:

{ 
    A original(10); 
    foo(original); 
} 

Bản gốc đã được bỏ đi sau khi thoát khỏi khối. Nó có thể được sử dụng một cách an toàn như một tham số.

Để có tốc độ tối ưu, hãy chuyển đối tượng theo tham chiếu, sử dụng biến tạm thời sẽ được trình biên dịch loại bỏ trong quá trình tối ưu hóa.

1

Trong biểu thức:

A a(foo(A(10))); 

Kết quả của các tiểu biểu A(10) là một rvalue loại A. (5.2.3 [expr.type.conv])

Khi khởi tạo một tham chiếu const từ một rvalue trình biên dịch có thể tạo ra một tạm thời từ rvalue và ràng buộc đó để tham chiếu. Ngay cả khi nó không chọn, thì hàm tạo bản sao phải có thể truy cập được. (8.5.3 [decl.init.ref]) Điều này sẽ không xảy ra nếu có tham chiếu đã được khởi tạo từ tương thích tham chiếulvalue nơi bắt buộc ràng buộc trực tiếp.

Khi foo lấy tham số của nó theo tham chiếu và không có giá trị, không có bản sao nào được ủy quyền để tự khởi tạo đối số.

foo trả về một int, vì vậy không có bản sao của A tại đây.

a được khởi tạo trực tiếp từ int được trả về bởi foo, vì vậy không có bản sao của A tại đây.