2012-04-24 10 views
8

Giả sử tôi có một lớp học nơi các nhà xây dựng bản sao là tư nhân và không được thực hiện (để làm cho đối tượng không copyable)RVO có được phép khi một hàm tạo bản sao ở chế độ riêng tư và không được triển khai không?

class NonCopyable { 
// whatever 
private: 
    NonCopyable(const NonCopyable&); 
    void operator=(const NonCopyable&); 
}; 

Bây giờ trong một số chức năng thành viên của lớp cùng tôi viết code mà trả về một đối tượng của lớp đó:.

NonCopyable NonCopyable::Something() 
{ 
    return NonCopyable(); 
} 

mà là một trường hợp khi RVO thể kick trong

RVO vẫn đòi hỏi một constructor sao chép có thể truy cập. Kể từ khi có thể gọi đến hàm tạo bản sao được thực hiện từ bên trong cùng một hàm thành viên lớp, hàm tạo bản sao có thể truy cập. Vì vậy, về mặt kỹ thuật RVO là có thể mặc dù thực tế rằng mục đích là để cấm sử dụng các nhà xây dựng bản sao.

Được phép sử dụng RVO trong những trường hợp như vậy?

Trả lời

3

Ví dụ của bạn khá thú vị.

Đây là khai báo C++ 03 điển hình.

class NC { 
public: 
    NC NC::Something() { 
     return NC(); 
    } 

private: 
    NC(NC const&); 
    NC& operator=(NC const&); 
}; 

Ở đây, như đã nêu, RVO thể kick trong mặc dù chúng ta ngữ nghĩa muốn tránh sao chép.

Trong C++ 03, giải pháp là ủy:

class NC: boost::noncopyable { 
public: 
    NC NC::Something() { // Error: no copy constructor 
     return NC(); 
    } 
}; 

Trong C++ 11, chúng tôi có giải pháp thay thế của việc sử dụng các delete keyword:

class NC { 
public: 
    NC NC::Something() { // Error: deleted copy constructor 
     return NC(); 
    } 

private: 
    NC(NC const&) = delete; 
    NC& operator=(NC const&) = delete; 
}; 

Nhưng đôi khi, chúng tôi muốn ngăn sao chép, nhưng muốn cho phép Builder (như trong Mẫu).

Trong trường hợp này, ví dụ của bạn hoạt động miễn là RVO đá ở, điều này hơi khó chịu vì thực chất là không chuẩn. Một định nghĩa của constructor sao chép nên được cung cấp nhưng bạn muốn nó không được sử dụng.

Trong C++ 11, usecase này được hỗ trợ bằng cách xóa thao tác sao chép và xác định hoạt động di chuyển (thậm chí riêng tư).

6

Có, RVO sẽ được cho phép trong trường hợp này - ít nhất là nếu người gọi Something() là thành viên hoặc bạn bè của lớp học.

Tôi nghĩ đây là một trong những lý do tại sao quyền thừa kế riêng tư của một lớp không thể sao chép tốt hơn so với thực hiện 'thủ công' trong mỗi lớp bạn muốn ngăn sao chép. Trong trường hợp đó không có lỗ hổng ngẫu nhiên.

Ví dụ, sử dụng boost::noncopyable:

class NonCopyable : private boost::noncopyable { 
public: 
    NonCopyable() {}; 
    NonCopyable Something(); 
}; 

NonCopyable NonCopyable::Something() 
{ 
    return NonCopyable(); // causes compile time error, not link time error 
} 
+3

Hoặc đơn giản bằng cách sử dụng các tiện ích C++ 11 'delete' để phân biệt rõ ràng ngay tại giai đoạn biên dịch (thay vì giai đoạn liên kết) mà lớp đó sẽ không được sao chép. –

+0

@Matthieu: Vâng, đó là một cơ chế tốt hơn nếu bạn không phải lo lắng về các trình biên dịch kế thừa (tôi đoán chúng ta có thể gọi chúng là di sản, ngay cả khi tôi chắc chắn rằng chúng vẫn có sự thống trị lớn trong cách sử dụng). –

0

Nó được cho phép, nhưng bạn có thể nhận được một lỗi liên kết bởi vì các nhà xây dựng bản sao không được thực hiện. Nếu bạn cung cấp nội dung cho NonCopyable(const NonCopyable&), nó sẽ hoạt động.

+1

Sẽ không có lỗi liên kết vì với RVO không có cuộc gọi thực sự nào tới bản sao ctor. –

+0

Có _may be_ một lỗi liên kết :) Ví dụ: nếu tối ưu hóa bị tắt. Tôi đã có lỗi trong MSVC ngay cả khi tối ưu hóa trên. – Andrey

+0

Michael vẫn đúng: Không có lỗi liên kết _if_ Tối ưu hóa giá trị trả lại đã được thực hiện (đó là kịch bản được thảo luận). Điều đó rõ ràng ngụ ý rằng tối ưu hóa cụ thể đó không bị tắt. – MSalters

0

Một điểm quan trọng có thể làm lu mờ câu hỏi thực tế.

What is the use case of such function if RVO is allowed ?

Chức năng này có thể được gọi trong 3 cách và 2 trong số đó sẽ là một lỗi biên dịch:

NonCopyable obj; 
NonCopyable obj2 = obj; // 1 --> error 
NonCopyable &r = obj; // 2 --> error 
const NonCopyable &rc = obj; // 3 --> ok, but dangerous (may lead to UB) 

Người ta không thể sử dụng đối tượng quay trở lại một cách hiệu quả, vì vậy nó thực sự không quan trọng nếu RVO có được phép hay không.