2013-08-17 51 views
54

Đây có phải làLà một hàm tạo di chuyển `= mặc định 'tương đương với một hàm tạo di chuyển thành viên khôn ngoan?

struct Example { 
    int a, b; 
    Example(int mA, int mB) : a{mA}, b{mB}    { } 
    Example(const Example& mE) : a{mE.a}, b{mE.b}  { } 
    Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { } 
    Example& operator=(const Example& mE) { a = mE.a; b = mE.b; return *this; } 
    Example& operator=(Example&& mE)  { a = move(mE.a); b = move(mE.b); return *this; } 
} 

tương đương này

struct Example { 
    int a, b; 
    Example(int mA, int mB) : a{mA}, b{mB} { } 
    Example(const Example& mE)   = default; 
    Example(Example&& mE)     = default; 
    Example& operator=(const Example& mE) = default; 
    Example& operator=(Example&& mE)  = default; 
} 

?

+0

Đây có thể là một sự trùng lặp của http://stackoverflow.com/questions/4819936/why-no-default-move-assignment-move-constructor –

+3

@ DieterLücking: Rõ ràng là không, mặc dù nó thuộc về một chủ đề tương tự và một số câu trả lời có thể bao gồm mặt bằng tương tự. Tuy nhiên, chúng tôi sẽ không đóng mọi câu hỏi đơn lẻ về ngữ nghĩa di chuyển như các bản sao của nhau. –

+0

Lưu ý, tôi đã thêm câu trả lời cho câu hỏi này bởi vì tại thời điểm tôi đang tìm kiếm một trích dẫn từ tiêu chuẩn đã chứng tỏ chúng tương đương và câu trả lời được chấp nhận không làm điều đó. Vì vậy, tôi chỉ tìm thấy báo giá và thêm câu trả lời của tôi. –

Trả lời

27

Có cả hai đều giống nhau.

Nhưng

struct Example { 
    int a, b; 
    Example(int mA, int mB) : a{mA}, b{mB} { } 
    Example(const Example& mE)   = default; 
    Example(Example&& mE)     = default; 
    Example& operator=(const Example& mE) = default; 
    Example& operator=(Example&& mE)  = default; 
} 

Phiên bản này sẽ cho phép bạn bỏ qua các định nghĩa cơ thể.

Tuy nhiên, bạn phải tuân theo một số quy tắc khi bạn khai báo explicitly-defaulted-functions:

8.4.2 chức năng Rõ ràng-defaulted [dcl.fct.def.default]

Một định nghĩa chức năng của hình thức:

attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = default ; 

được gọi là một rõ ràng-defaulted nét. Một chức năng mà một cách rõ ràng được mặc định sẽ

  • là một hàm thành viên đặc biệt,

  • có cùng một loại chức năng khai báo (ngoại trừ có thể khác nhau ref-vòng loại và ngoại trừ trong trường hợp của một bản sao nhà điều hành gán hoặc bản sao, kiểu tham số có thể là "tham chiếu đến không const T", trong đó T là tên của lớp của hàm thành viên) như thể nó đã được khai báo hoàn toàn,

  • không có đối số mặc định.

+0

Bạn trích dẫn 8.4.2 từ tài liệu nào? Cả chuẩn C++ 11 hoặc N3690 đều không chứa văn bản ", và không có * ngoại lệ * * trong 8.4.2/1. Cả hai đều nói trong 8.4.2/2: "Một hàm mặc định rõ ràng có thể được khai báo' constexpr' chỉ khi nó được khai báo ngầm định là 'constexpr' và có thể có một đặc tả * ngoại lệ rõ ràng * nếu nó là tương thích (15.4) với * đặc tả ngoại lệ * trên khai báo ngầm. " – Casey

+1

@Casey Tốt bắt! Tôi đã trích dẫn N3242 ... Tôi trộn tài liệu của mình ... Tôi đã cập nhật bài đăng của mình để trích dẫn N3690! Cám ơn bạn vì đã chỉ ra điều này ! –

+1

Nếu tôi đặt một hàm khởi tạo và toán tử gán cho '= default', tôi có thể hoán đổi với đối tượng không? Tôi không cần phải khai báo constructor là 'noexcept'? Tôi đã thử đặt cả hai 'noexcept' và' = default' cho cả hai, nhưng điều này sẽ không biên dịch. – VF1

0

ngoài các trường hợp bệnh lý rất ... CÓ.

Để chính xác hơn, bạn cũng phải xem xét các cơ sở cuối cùng Example có thể có, với các quy tắc tương tự. Đầu tiên là lệnh khai báo căn cứ- rồi các thành viên, luôn theo thứ tự khai báo.

14

Vâng, một constructor chuyển defaulted sẽ thực hiện một động thái thành viên khôn ngoan của cơ sở và các thành viên của nó, vì vậy:

Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { } 

tương đương với:

Example(Example&& mE)     = default; 

chúng ta có thể xem điều này bằng cách đi tới phần draft C++11 standard12.8Sao chép và di chuyển đối tượng lớp học paragra ph mà nói (tôi nhấn mạnh đi về phía trước):

Một constructor sao chép/di chuyển mà được mặc định và không được định nghĩa như xóa ngầm được xác định nếu nó được odrused (3.2) hoặc khi nó rõ ràng là được mặc định sau lần khai báo đầu tiên. [Lưu ý: Bản sao/di chuyển hàm tạo được xác định hoàn toàn ngay cả khi việc triển khai được ưu tiên mức độ sử dụng của nó (3.2, 12.2). lưu ý end] [...]

và đoạn mà nói:

Các copy ngầm xác định/di chuyển constructor cho một lớp phi đoàn X thực hiện một sao chép thành viên/di chuyển các căn cứ và thành viên của nó. [Lưu ý: bộ khởi tạo dấu ngoặc đơn hoặc bằng nhau của các thành viên dữ liệu không tĩnh sẽ bị bỏ qua. Xem thêm ví dụ trong 12.6.2. —end note] Thứ tự của việc khởi tạo giống như thứ tự khởi tạo các căn cứ và thành viên trong một hàm tạo do người dùng xác định (xem 12.6.2). Gọi x là tham số của hàm tạo hoặc đối với hàm tạo di chuyển, một tham số tham chiếu đến tham số. Mỗi cơ sở dữ liệu hoặc không tĩnh thành viên được sao chép/di chuyển theo cách thức phù hợp với loại của nó:

  • nếu hội viên là một mảng, mỗi phần tử là trực tiếp khởi tạo với các subobject tương ứng của x;
  • nếu một thành viên m có loại tham chiếu rvalue T & &, nó được khởi tạo trực tiếp với static_cast (x.m);
  • nếu không, cơ sở hoặc thành viên được khởi tạo trực tiếp với cơ sở hoặc thành viên tương ứng của x.

Các lớp con ảo lớp cơ sở phải được khởi tạo chỉ một lần bởi hàm tạo sao chép/di chuyển được xác định rõ ràng (xem 12.6.2).

0

là tương đương này để này

Yes. Nhưng nó chỉ hợp lệ cho các lớp không có thành viên không di chuyển được.Nhìn vào ví dụ này:

#include <iostream> 

struct nonmovable 
{ 
    nonmovable() = default; 

    nonmovable(const nonmovable &) = default; 
    nonmovable(  nonmovable &&) = delete; 
}; 

struct movable 
{ 
    movable() = default; 

    movable(const movable &) { std::cerr << "copy" << std::endl; } 
    movable(  movable &&) { std::cerr << "move" << std::endl; } 
}; 

struct has_nonmovable 
{ 
    movable a; 
    nonmovable b; 

    has_nonmovable() = default; 

    has_nonmovable(const has_nonmovable &) = default; 
    has_nonmovable(  has_nonmovable &&) = default; 
}; 

int main() 
{ 
    has_nonmovable c; 
    has_nonmovable d(std::move(c)); // prints copy 
} 

It in:

copy 

http://coliru.stacked-crooked.com/a/62c0a0aaec15b0eb

Vì vậy, nếu một lớp có thậm chí là một thành viên không thể di chuyển duy nhất, các nhà xây dựng di chuyển mặc định một cách rõ ràng sẽ sử dụng các nhà thầu bản sao cho tất cả các thành viên, ngay cả đối với những người có công cụ xây dựng di chuyển.

Nhưng các thành viên không bắt buộc phải có các nhà thầu di chuyển được xác định rõ ràng hoặc được mặc định rõ ràng. Nếu constructor di chuyển không được khai báo, thì hàm tạo di chuyển sẽ được sử dụng cho các thành viên có hàm khởi tạo di chuyển và hàm tạo bản sao sẽ được sử dụng cho các thành viên không xác định hàm tạo di chuyển. Xem ví dụ:

#include <iostream> 

struct nonmovable 
{ 
    nonmovable() = default; 

    nonmovable(const nonmovable &) { std::cerr << "nonmovable::copy" << std::endl; } 
    //nonmovable(  nonmovable &&) = delete; 
}; 

struct movable 
{ 
    movable() = default; 

    movable(const movable &) { std::cerr << "movable::copy" << std::endl; } 
    movable(  movable &&) { std::cerr << "movable::move" << std::endl; } 
}; 

struct has_nonmovable 
{ 
    movable a; 
    nonmovable b; 

    has_nonmovable() = default; 

    has_nonmovable(const has_nonmovable &) = default; 
    has_nonmovable(  has_nonmovable &&) = default; 
}; 

int main() 
{ 
    has_nonmovable c; 
    has_nonmovable d(std::move(c)); 
} 

It in:

movable::move 
nonmovable::copy 

http://coliru.stacked-crooked.com/a/fda51f29a4b92881

Có các lớp học được di chuyển nhưng không copyable (ví dụ unique_ptr). Có các lớp có thể sao chép và sử dụng hàm tạo bản sao để di chuyển. Nhưng tôi không biết bất kỳ lý do nào để tạo một lớp có thể sao chép được nhưng đã xóa một cách rõ ràng hàm khởi tạo.