Tại sao các hàm bị xóa trong C ++ 11 lại tham gia vào việc giải quyết quá tải?


87

Tại sao C ++ 11 làm cho các hàm " deleted" tham gia vào việc phân giải quá tải ?
Tại sao điều này lại hữu ích? Hay nói cách khác, tại sao chúng lại bị ẩn thay vì bị xóa hoàn toàn?



Câu trả lời:


114

Một nửa mục đích của = deletecú pháp là có thể ngăn mọi người gọi một số hàm với các tham số nhất định. Điều này chủ yếu là để ngăn chặn chuyển đổi ngầm trong một số trường hợp cụ thể. Để cấm quá tải cụ thể, nó phải tham gia vào việc giải quyết quá tải.

Câu trả lời mà bạn trích dẫn cho bạn một ví dụ hoàn hảo:

struct onlydouble {
  onlydouble(std::intmax_t) = delete;
  onlydouble(double);
};

Nếu deleteloại bỏ hoàn toàn hàm, điều đó sẽ làm cho = deletecú pháp tương đương như sau:

struct onlydouble2 {
  onlydouble2(double);
};

Bạn có thể làm điều này:

onlydouble2 val(20);

Đây là C ++ hợp pháp. Trình biên dịch sẽ xem xét tất cả các hàm tạo; không ai trong số họ trực tiếp lấy kiểu số nguyên. Nhưng một trong số họ có thể lấy nó sau khi chuyển đổi ngầm. Vì vậy, nó sẽ gọi như vậy.

onlydouble val(20);

Đây không phải là C ++ hợp pháp. Trình biên dịch sẽ xem xét tất cả các hàm tạo, bao gồm cả các hàm deleted. Nó sẽ thấy một kết quả khớp chính xác, thông qua std::intmax_t(sẽ khớp chính xác với bất kỳ chữ số nguyên nào). Vì vậy, trình biên dịch sẽ chọn nó và sau đó ngay lập tức đưa ra lỗi, vì nó đã chọn một deletehàm d.

= deletecó nghĩa là "Tôi cấm điều này," không chỉ đơn thuần, "Điều này không tồn tại." Đó là một tuyên bố mạnh mẽ hơn nhiều.

Tôi đã hỏi tại sao tiêu chuẩn C ++ nói = xóa có nghĩa là "Tôi cấm điều này" thay vì "điều này không tồn tại"

Đó là bởi vì chúng ta không cần ngữ pháp đặc biệt để nói "điều này không tồn tại." Chúng tôi hiểu ngầm điều này bằng cách không khai báo "this" cụ thể được đề cập. "Tôi cấm điều này" đại diện cho một cấu trúc không thể đạt được nếu không có ngữ pháp đặc biệt. Vì vậy, chúng tôi nhận được ngữ pháp đặc biệt để nói "Tôi cấm điều này" chứ không phải điều khác.

Chức năng duy nhất mà bạn có được khi có một ngữ pháp rõ ràng "cái này không tồn tại" sẽ là ngăn ai đó sau này tuyên bố nó tồn tại. Và điều đó không đủ hữu ích để cần có ngữ pháp riêng.

nếu không thì không có cách nào để tuyên bố rằng hàm tạo bản sao không tồn tại và sự tồn tại của nó có thể gây ra sự mơ hồ vô nghĩa.

Hàm tạo bản sao là một hàm thành viên đặc biệt. Mỗi lớp luôn có một hàm tạo bản sao. Cũng giống như chúng luôn có toán tử gán bản sao, hàm tạo di chuyển, v.v.

Các chức năng này tồn tại; câu hỏi chỉ là liệu việc gọi họ có hợp pháp hay không. Nếu bạn cố gắng nói điều đó = deletecó nghĩa là chúng không tồn tại, thì thông số kỹ thuật sẽ phải giải thích ý nghĩa của việc một hàm không tồn tại. Đây không phải là một khái niệm mà đặc tả xử lý.

Nếu bạn cố gắng gọi một hàm chưa được khai báo / định nghĩa, thì trình biên dịch sẽ bị lỗi. Nhưng nó sẽ lỗi do một số nhận dạng không xác định , không phải do lỗi "hàm không tồn tại" (ngay cả khi trình biên dịch của bạn báo cáo theo cách đó). Các hàm tạo khác nhau đều được gọi bằng độ phân giải quá tải, do đó, "sự tồn tại" của chúng được xử lý về mặt đó.

Trong mọi trường hợp, có một hàm được khai báo thông qua mã định danh hoặc một hàm tạo / hủy (cũng được khai báo thông qua mã định danh, chỉ là một mã định danh kiểu). Việc nạp chồng toán tử ẩn định danh đằng sau đường cú pháp, nhưng nó vẫn ở đó.

Đặc tả C ++ không thể xử lý khái niệm "hàm không tồn tại". Nó có thể xử lý sự không khớp quá tải. Nó có thể xử lý sự mơ hồ quá tải. Nhưng nó không biết về những gì không có ở đó. Vì vậy, = deleteđược định nghĩa theo nghĩa "nỗ lực gọi đây là thất bại" hữu ích hơn nhiều so với "giả vờ như tôi chưa bao giờ viết dòng này".

Và một lần nữa, hãy đọc lại phần đầu tiên. Bạn không thể làm điều đó với "chức năng không tồn tại." Đó là một lý do khác tại sao nó được định nghĩa theo cách đó: bởi vì một trong những trường hợp sử dụng chính của = deletecú pháp là có thể buộc người dùng sử dụng một số loại tham số nhất định, ép kiểu rõ ràng, v.v. Về cơ bản, để loại bỏ các chuyển đổi kiểu ngầm.

Đề xuất của bạn sẽ không làm được điều đó.


1
@Mehrdad: Nếu bạn cần giải thích rõ hơn về lý do tại sao = delete không hoạt động theo cách bạn muốn, bạn cần phải nói rõ hơn về ngữ nghĩa chính xác mà bạn nghĩ = delete nên có. Có nên "giả vờ như tôi chưa từng viết dòng này không?" Hay nó phải là một cái gì đó khác?
Nicol Bolas

1
Không, ý tôi là tôi dự kiến = deletecó nghĩa là "thành viên này không tồn tại", có nghĩa là nó không thể tham gia giải quyết quá tải.
user541686

6
@Mehrdad: Và điều đó quay trở lại quan điểm ban đầu của tôi, đó là lý do tại sao tôi đăng nó: nếu = deletecó nghĩa là "thành viên này không tồn tại", thì ví dụ đầu tiên tôi đã đăng sẽ không thể ngăn mọi người chuyển số nguyên sang hàm tạo onlydoublecủa , vì onlydoublequá tải bị xóa sẽ không tồn tại . Nó sẽ không tham gia vào quá trình phân giải quá tải, và do đó nó sẽ không ngăn bạn chuyển các số nguyên. Đó là một nửa điểm của = deletecú pháp: để có thể nói, "Bạn không thể chuyển X ngầm định cho hàm này."
Nicol Bolas

3
@Mehrdad: Theo logic đó, tại sao bạn lại cần =delete? Rốt cuộc, chúng ta có thể nói "không thể sao chép" bằng cách thực hiện điều tương tự: khai báo hàm tạo / phép gán bản sao là riêng tư. Ngoài ra, lưu ý rằng việc khai báo điều gì đó riêng tư không làm cho nó không thể gọi được; mã trong lớp vẫn có thể gọi nó. Vì vậy, nó không giống như = delete. Không, = deletecú pháp cho phép chúng ta thực hiện một điều gì đó rất bất tiện và khó hiểu trước đây theo cách rõ ràng và hợp lý hơn nhiều.
Nicol Bolas,

2
@Mehrdad: Bởi vì điều sau có thể xảy ra : nó được gọi là "không khai báo". Sau đó, nó sẽ không tồn tại. Về việc tại sao chúng ta cần cú pháp để ẩn quá tải thay vì lạm dụng chế độ riêng tư ... bạn thực sự hỏi tại sao chúng ta nên có một phương tiện để trình bày rõ ràng điều gì đó, thay vì lạm dụng một số tính năng khác để có được hiệu quả tương tự ? Nó chỉ đơn giản là rõ ràng hơn cho bất kỳ ai đọc mã những gì đang xảy ra. Nó làm cho mã dễ hiểu hơn đối với người dùng và giúp viết dễ dàng hơn, đồng thời khắc phục các sự cố trong cách giải quyết khác. Chúng tôi cũng không cần lambdas.
Nicol Bolas,

10

Dự thảo làm việc C ++ 2012-11-02 không cung cấp cơ sở lý luận đằng sau quy tắc này, chỉ là một số ví dụ

8.4.3 Các định nghĩa đã xóa [dcl.fct.def.delete]
...
3 [ Ví dụ : Người ta có thể thực thi khởi tạo không mặc định và khởi tạo không tích phân với

struct onlydouble {  
  onlydouble() = delete; // OK, but redundant  
  onlydouble(std::intmax_t) = delete;  
  onlydouble(double);  
};  

- end example ]
[ Ví dụ : Người ta có thể ngăn chặn việc sử dụng một lớp trong các biểu thức mới nhất định bằng cách sử dụng các định nghĩa đã xóa của toán tử do người dùng khai báo mới cho lớp đó.

struct sometype {  
  void *operator new(std::size_t) = delete;  
  void *operator new[](std::size_t) = delete;  
};  
sometype *p = new sometype; // error, deleted class operator new  
sometype *q = new sometype[3]; // error, deleted class operator new[]  

- end example ]
[ Ví dụ : Người ta có thể làm cho một lớp không thể sao chép, tức là chỉ di chuyển, bằng cách sử dụng các định nghĩa đã xóa của hàm tạo bản sao và toán tử gán sao chép, sau đó cung cấp các định nghĩa mặc định của hàm tạo di chuyển và toán tử gán di chuyển.

struct moveonly {  
  moveonly() = default;  
  moveonly(const moveonly&) = delete;  
  moveonly(moveonly&&) = default;  
  moveonly& operator=(const moveonly&) = delete;  
  moveonly& operator=(moveonly&&) = default;  
  ~moveonly() = default;  
};  
moveonly *p;  
moveonly q(*p); // error, deleted copy constructor  

- cuối ví dụ ]


4
Cơ sở lý luận có vẻ rất rõ ràng từ các ví dụ, bạn có nghĩ vậy không?
Jesse Good
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.