2016-02-04 19 views
5

Trong C++ 11, người ta có thể mặc định rõ ràng một hàm thành viên đặc biệt, nếu thế hệ ngầm của nó được tự động ngăn chặn.Thực thi một cách rõ ràng chức năng thành viên đặc biệt mặc định thành viên

Tuy nhiên, mặc định rõ ràng chức năng thành viên đặc biệt chỉ hoàn tác việc xóa ngầm định bằng cách khai báo thủ công một số hàm thành viên đặc biệt khác (thao tác sao chép, hủy, vv), không buộc trình biên dịch tạo ra hàm và mã được coi là được hình thành tốt ngay cả khi chức năng không thể được tạo ra.

Hãy xem xét kịch bản sau đây:

struct A 
{ 
    A()   = default; 
    A (const A&) = default; 
    A (A&&)  = delete; // Move constructor is deleted here 
}; 

struct B 
{ 
    B()   = default; 
    B (const B&) = default; 
    B (B&&)  = default; // Move constructor is defaulted here 

    A a; 
}; 

Các nhà xây dựng di chuyển trong B sẽ không được tạo ra bởi trình biên dịch, bởi vì làm như vậy sẽ gây ra một lỗi biên dịch (A của constructor di chuyển sẽ bị xóa). Nếu không xóa rõ ràng hàm tạo của A, hàm tạo của B sẽ được tạo như mong đợi (sao chép A, thay vì di chuyển nó).

Cố gắng di chuyển như một đối tượng âm thầm sẽ sử dụng constructor sao chép thay vì:

B b; 
B b2 (std::move(b)); // Will call B's copy constructor 

Có cách nào để buộc các trình biên dịch vào hoặc tạo ra các chức năng hoặc phát hành một lỗi biên dịch nếu nó có thể không? Nếu không có sự đảm bảo này, rất khó để dựa vào các nhà xây dựng di chuyển mặc định, nếu một nhà xây dựng đã xóa duy nhất có thể vô hiệu hóa di chuyển cho toàn bộ hệ thống phân cấp đối tượng.

+0

Bạn không nên biết liệu các thành viên bạn có bao gồm có thể di chuyển được hay không? – NathanOliver

+2

Chúng có thể di chuyển khi lớp được triển khai ban đầu, tuy nhiên, nếu có thêm thành viên nào được thêm vào sau, yêu cầu di chuyển có thể bị bỏ qua (đặc biệt nếu chúng được thêm bởi người khác). –

+1

Cần lưu ý rằng một loại mà xóa constructor di chuyển của nó nhưng * không * constructor sao chép của nó là rất ... kỳ quái. Hoàn toàn không có gì để đạt được bằng cách làm điều này. Vì vậy, tốt nhất nên bỏ qua trường hợp này, coi đó là kết quả của một người làm điều ngu ngốc không có lý do gì. –

Trả lời

3

Có một cách để phát hiện các loại như A. Nhưng chỉ khi loại rõ ràng xóa hàm khởi tạo di chuyển. Nếu hàm tạo di chuyển được tạo ngầm như đã xóa, thì nó sẽ không tham gia vào quá trình phân giải quá tải. Đây là lý do tại sao B có thể di chuyển mặc dù A thì không. Bdefault s nhà xây dựng di chuyển, có nghĩa là nó bị xóa hoàn toàn, do đó việc sao chép diễn ra.

B do đó di chuyển có thể cấu hình được. Tuy nhiên, A thì không. Vì vậy, đó là một vấn đề đơn giản của việc này:

struct B 
{ 
    static_assert(is_move_constructible<A>::value, "Oops..."); 

    B()   = default; 
    B (const B&) = default; 
    B (B&&)  = default; // Move constructor is defaulted here 

    A a; 
}; 

Bây giờ, không có chung cách để gây ra bất kỳ loại, trong đó có bản sao chỉ các loại để làm những gì bạn muốn. Tức là, bạn phải khẳng định tĩnh trên từng loại riêng lẻ; bạn không thể đặt một số cú pháp trong hàm khởi tạo di chuyển mặc định để cố gắng di chuyển B không thành công.

Lý do cho việc đó phải làm một phần với khả năng tương thích ngược. Hãy suy nghĩ về tất cả các mã pre-C++ 11 đã khai báo các hàm tạo bản sao do người dùng định nghĩa. Theo các quy tắc của việc tạo ra các hàm tạo dựng trong C++ 11, tất cả chúng sẽ có các hàm khởi tạo di chuyển. Có nghĩa là bất kỳ mã nào của biểu mẫu T t = FuncReturningTByValue(); sẽ không thành công, mặc dù nó hoạt động tốt trong C++ 98/03 bằng cách gọi hàm tạo bản sao. Vì vậy, vấn đề di chuyển theo bản sao đã làm việc xung quanh vấn đề này bằng cách tạo các bản sao này thay vì di chuyển nếu không thể tạo hàm tạo di chuyển.

Nhưng vì = default có nghĩa là "làm những gì bạn thường làm", nó cũng bao gồm hành vi phân giải quá tải đặc biệt này bỏ qua hàm khởi động di chuyển bị xóa hoàn toàn.

Các vấn đề liên quan