2013-03-26 41 views
30

Tại sao trình biên dịch C++ có nhiều hạn chế hơn đối với các hàm tạo di chuyển được tạo tự động hơn là trên trình tạo bản sao được tạo tự động hoặc toán tử gán?Trình dịch chuyển di chuyển mặc định so với hàm tạo bản sao mặc định so với toán tử gán mặc định

constructors di chuyển Tự động tạo được tạo ra chỉ khi người sử dụng đã được xác định không có gì (ví dụ: nhà xây dựng, sao chép, chuyển nhượng, destructor ..)

Sao chép constructor hoặc toán tử gán được tạo ra chỉ khi người sử dụng đã không được định nghĩa tương ứng copy constructor hoặc toán tử gán.

Tôi tự hỏi tại sao sự khác biệt.

+1

Từ quan điểm chỉ khái niệm (hãy cẩn thận, không có giải thích kỹ thuật), cả hai đều không bằng nhau, ví dụ: mọi đối tượng có thể sao chép đều có thể di chuyển nhưng không phải là cách khác. Di chuyển là trường hợp đặc biệt của việc sao chép áp dụng trong một số trường hợp nhất định. Vì vậy, nó có ý nghĩa rằng hành vi autogeneration của họ là bằng cách nào đó sai lệch. Nhưng tôi để nó cho người khác để dịch nó thành tiêu chuẩn kỹ thuật với những ví dụ thích hợp. Khác hơn là câu hỏi tốt, tất nhiên. –

Trả lời

13

Tôi tin rằng khả năng tương thích ngược đóng một vai trò lớn ở đây. Nếu người dùng định nghĩa bất kỳ chức năng "Quy tắc ba" nào (bản sao ctor, sao chép gán op, dtor), có thể giả định rằng lớp đó thực hiện một số quản lý tài nguyên nội bộ. Việc xác định ngầm một hàm khởi tạo có thể đột nhiên làm cho lớp không hợp lệ khi được biên dịch trong C++ 11.

Hãy xem xét ví dụ sau:

class Res 
{ 
    int *data; 

public: 
    Res() : data(new int) {} 

    Res(const Res &arg) : data(new int(*arg.data)) {} 

    ~Res() { delete data; } 
}; 

Bây giờ nếu một constructor di chuyển mặc định đã được tạo ra cho lớp này, gọi nó sẽ dẫn đến một xóa đôi của data.

Đối với toán tử gán di chuyển ngăn chặn các định nghĩa hàm khởi tạo mặc định: nếu toán tử gán di chuyển thực hiện điều gì đó khác với mặc định, rất có thể sẽ sai khi sử dụng hàm khởi tạo mặc định. Đó chỉ là "Quy tắc ba"/"Quy tắc năm" có hiệu lực.

9

Theo như tôi biết, điều này là do tính tương thích xuống. Xem xét các lớp được viết bằng C++ (trước C++ 11) và điều gì sẽ xảy ra nếu C++ 11 sẽ bắt đầu tự động tạo các trình di chuyển-ctors song song với các bản sao hiện có hoặc thường là bất kỳ ctor nào khác. Nó sẽ dễ dàng phá vỡ mã hiện tại, bằng cách truyền bản sao-ctor mà tác giả của lớp đó đã viết. Do đó, các quy tắc để tạo ra một di chuyển-ctor nơi crafted để chỉ áp dụng cho "an toàn" trường hợp.

Đây là bài viết từ Dave Abrahams về why implicit move must go, cuối cùng dẫn đến các quy tắc hiện tại của C++ 11.

Và đây là một ví dụ làm thế nào nó sẽ thất bại:

// NOTE: This example assumes an implicitly generated move-ctor 

class X 
{ 
private:  
    std::vector<int> v; 

public: 
    // invariant: v.size() == 5 
    X() : v(5) {} 

    ~X() 
    { 
     std::cout << v[0] << std::endl; 
    } 
}; 

int main() 
{ 
    std::vector<X> y; 

    // and here is where it would fail: 
    // X() is an rvalue: copied in C++03, moved in C++0x 
    // the classes' invariant breaks and the dtor will illegally access v[0]. 
    y.push_back(X()); 
} 
+0

+1 cho bài viết! –

9

Khi C++ được tạo, nó đã được quyết định là hàm tạo mặc định, sao chép-khởi tạo, phép gán và hàm hủy sẽ được tạo tự động (trừ khi được cung cấp). Tại sao ? Bởi vì các trình biên dịch C++ có thể biên dịch (đa số) mã C với các ngữ nghĩa giống nhau, và đó là cách struct hoạt động trong C.

Tuy nhiên, sau này người dùng viết một trình phá hủy tùy chỉnh, có lẽ cô ấy cần phải viết tùy chỉnh copy-constructor/assignment-operator quá; điều này được gọi là Rule of Big Three. Với hindsight, chúng ta có thể thấy rằng có thể chỉ định rằng copy-constructor/assign-operator/destructor được tạo ra sẽ chỉ được tạo nếu không có 3 trong số đó được người dùng cung cấp, và nó có thể đã bắt được rất nhiều lỗi. .. và vẫn giữ lại khả năng tương thích ngược với C.

Vì vậy, khi C++ 11 xuất hiện, nó đã được quyết định rằng thời gian này sẽ được thực hiện ngay: di chuyển mới-constructor và di chuyển-giao-điều hành sẽ chỉ được được tạo tự động nếu rõ ràng là người dùng không làm bất cứ điều gì "đặc biệt" với lớp học. Bất cứ điều gì "đặc biệt" được định nghĩa là xác định lại hành vi di chuyển/sao chép/hủy diệt.

Để giúp đỡ trường hợp, mọi người sẽ làm điều gì đó đặc biệt nhưng vẫn muốn phương pháp đặc biệt "được tạo tự động", lớp phủ đường = default cũng được thêm vào.

Thật không may, vì lý do tương thích ngược, ủy ban C++ không thể quay ngược thời gian và thay đổi quy tắc tự động tạo bản sao; Tôi ước rằng họ đã không dùng nó để mở đường cho phiên bản tiếp theo của tiêu chuẩn, nhưng tôi nghi ngờ họ sẽ làm như vậy. Tuy nhiên, không được chấp nhận nữa (xem §12.8/7 đối với hàm tạo bản sao chẳng hạn, lịch sự của @Nevin).

+0

Tôi chưa bao giờ nghe nó được gọi là "Quy tắc của Big Three" –

+0

@LightnessRacesinOrbit: Đã thêm liên kết :) –

+0

"Quy tắc ba" plzkthx –

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