Không có nghĩa mặc định của Nhật Bản có nghĩa là gì sau khi khai báo hàm của lớp?


221

Tôi đã thấy defaultđược sử dụng bên cạnh các khai báo hàm trong một lớp. Nó làm gì?

class C {
  C(const C&) = default;
  C(C&&) = default;
  C& operator=(const C&) & = default;
  C& operator=(C&&) & = default;
  virtual ~C() { }
};

26
"&" Trước "=" trong khai báo toán tử gán làm gì?
dshin

Câu trả lời:


249

Đây là một tính năng mới của C ++ 11 .

Điều đó có nghĩa là bạn muốn sử dụng phiên bản do trình biên dịch tạo ra, vì vậy bạn không cần chỉ định phần thân.

Bạn cũng có thể sử dụng = deleteđể chỉ định rằng bạn không muốn trình biên dịch tự động tạo chức năng đó.

Với sự ra đời của các hàm tạo di chuyển và các toán tử gán chuyển động, các quy tắc khi các phiên bản tự động của các hàm tạo, hàm hủy và toán tử gán được tạo ra đã trở nên khá phức tạp. Sử dụng = default= deletelàm cho mọi thứ dễ dàng hơn khi bạn không cần phải nhớ các quy tắc: bạn chỉ cần nói những gì bạn muốn xảy ra.


17
= deletemạnh hơn: Có nghĩa là, sử dụng chức năng đó bị cấm, mặc dù nó vẫn tham gia vào độ phân giải quá tải.
Ded repeatator

2
Nhưng, nếu chúng ta muốn sử dụng trình biên dịch tạo định nghĩa, thì chúng ta không nên bỏ qua việc viết hàm đó thay vì "trước tiên hãy viết nó và sau đó gán nó cho mặc định"?
Mayank Jindal

47

Đây là một tính năng C ++ 0x mới cho trình biên dịch tạo phiên bản mặc định của hàm tạo hoặc toán tử gán tương ứng, tức là một tính năng chỉ thực hiện hành động sao chép hoặc di chuyển cho mỗi thành viên. Điều này hữu ích vì hàm tạo di chuyển không phải lúc nào cũng được tạo theo mặc định (ví dụ: nếu bạn có hàm hủy tùy chỉnh), không giống như hàm tạo sao chép (và tương tự như vậy đối với phép gán), nhưng nếu không có gì không tầm thường để viết, thì tốt hơn là để trình biên dịch xử lý nó hơn là đánh vần nó mỗi lần.

Cũng lưu ý rằng một hàm tạo mặc định sẽ không được tạo nếu bạn cung cấp bất kỳ hàm tạo không mặc định nào khác. Nếu bạn vẫn muốn hàm tạo mặc định, bạn có thể sử dụng cú pháp này để trình biên dịch tạo một.

Như một trường hợp sử dụng khác, có một số tình huống trong đó một hàm tạo sao chép sẽ không được tạo hoàn toàn (ví dụ: nếu bạn cung cấp một hàm tạo di chuyển tùy chỉnh). Nếu bạn vẫn muốn phiên bản mặc định, bạn có thể yêu cầu nó với cú pháp này.

Xem Phần 12.8 của tiêu chuẩn để biết chi tiết.


5
Mặc dù nó không chỉ dành cho các nhà xây dựng và bài tập, mà còn áp dụng cho operator new/new[], operator delete/delete[]và quá tải của chúng.
Sebastian Mach

21

Nó là mới trong C ++ 11, xem ở đây . Nó có thể khá hữu ích nếu bạn đã xác định một hàm tạo, nhưng muốn sử dụng mặc định cho các hàm khác. Pre-C ++ 11 bạn phải xác định tất cả các hàm tạo sau khi bạn đã xác định một hàm, ngay cả khi chúng tương đương với các giá trị mặc định.

Cũng lưu ý rằng trong một số trường hợp nhất định, không thể cung cấp hàm tạo mặc định do người dùng xác định, hoạt động giống như trình biên dịch được tổng hợp theo trình biên dịch mặc định và khởi tạo giá trị . defaultcho phép bạn lấy lại hành vi đó


5
liên quan đến đoạn thứ hai, bạn có thể cung cấp một ví dụ?
John Smith

11

Một trường hợp sử dụng khác mà tôi không thấy được đề cập trong các câu trả lời này là nó dễ dàng cho phép bạn thay đổi mức độ hiển thị của hàm tạo. Ví dụ: có thể bạn muốn một lớp bạn bè có thể truy cập vào hàm tạo sao chép, nhưng bạn không muốn nó được công khai.


1

Dự thảo tiêu chuẩn C ++ 17 N4659

https://github.com/cplusplus/draft/blob/master/ con / n4659.pdf 11.4.2 "Các hàm mặc định rõ ràng":

1 Định nghĩa hàm của biểu mẫu:

attribute-specifier-seq opt decl-specifier-seq opt declarator virt-specifier-seq opt = default ;

được gọi là một định nghĩa mặc định rõ ràng. Một chức năng được mặc định rõ ràng sẽ

  • (1.1) - là một chức năng thành viên đặc biệt,

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

  • (1.3) - không có đối số mặc định.

2 Hàm mặc định rõ ràng không được xác định là đã xóa chỉ có thể được khai báo constexpr nếu nó được khai báo ngầm là constexpr. Nếu một hàm được mặc định rõ ràng trong khai báo đầu tiên của nó, thì nó được coi là ngầm định nếu khai báo ngầm sẽ là.

3 Nếu một chức năng được mặc định rõ ràng được khai báo với một bộ xác định noexcept không tạo ra đặc tả ngoại lệ giống như khai báo ngầm (18.4), thì

  • (3.1) - nếu chức năng được mặc định rõ ràng trong khai báo đầu tiên, nó được định nghĩa là đã xóa;

  • (3.2) - mặt khác, chương trình không được định hình.

4 [Ví dụ:

struct S {
  constexpr S() = default;            // ill-formed: implicit S() is not constexpr
  S(int a = 0) = default;             // ill-formed: default argument
  void operator=(const S&) = default; // ill-formed: non-matching return type
  ~ S() noexcept(false) = default;    // deleted: exception specification does not match
private:
  int i;                              // OK: private copy constructor
  S(S&);
};
S::S(S&) = default;                   // OK: defines copy constructor

- ví dụ kết thúc]

5 Các hàm được mặc định rõ ràng và các hàm được khai báo ngầm được gọi chung là các hàm mặc định và việc triển khai sẽ cung cấp các định nghĩa ngầm cho chúng (15.1 15.4, 15.8), có nghĩa là xác định chúng là đã bị xóa. Một hàm được cung cấp bởi người dùng nếu nó được khai báo bởi người dùng và không được mặc định hoặc xóa rõ ràng trên khai báo đầu tiên của nó. Hàm được mặc định rõ ràng do người dùng cung cấp (nghĩa là được mặc định rõ ràng sau khi khai báo đầu tiên) được xác định tại điểm mà nó được mặc định rõ ràng; nếu một chức năng như vậy được định nghĩa ngầm là bị xóa, chương trình sẽ không được định dạng. [Lưu ý: Khai báo một hàm như mặc định sau khai báo đầu tiên của nó có thể cung cấp thực thi hiệu quả và định nghĩa ngắn gọn trong khi cho phép giao diện nhị phân ổn định cho cơ sở mã đang phát triển. - lưu ý cuối]

6 [Ví dụ:

struct trivial {
  trivial() = default;
  trivial(const trivial&) = default;
  trivial(trivial&&) = default;
  trivial& operator=(const trivial&) = default;
  trivial& operator=(trivial&&) = default;
  ~ trivial() = default;
};
struct nontrivial1 {
  nontrivial1();
};
nontrivial1::nontrivial1() = default;       // not first declaration

- ví dụ kết thúc]

Sau đó, câu hỏi là tất nhiên các chức năng có thể được khai báo ngầm và khi nào điều đó xảy ra, mà tôi đã giải thích tại:

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.