Ý nghĩa của = xóa sau khi khai báo hàm


241
class my_class
{
    ...
    my_class(my_class const &) = delete;
    ...
};

Điều đó = deletecó nghĩa gì trong bối cảnh đó?

Có bất kỳ "sửa đổi" nào khác (ngoài = 0= delete) không?


23
@Blindy Nó sẽ là tiêu chuẩn trong C ++ 0x, tức là sớm thôi.
Konrad Rudolph

1
Tôi đứng chính xác, tôi đã bỏ lỡ tính năng C ++ 0x này. Tôi đã nghĩ rằng đó là một #definela Qt được đánh giá là 0 và sau đó khai báo một hàm ẩn hoặc một cái gì đó.
Blindy

Tôi có một hồi ức về từ khóa 'vô hiệu hóa' có nghĩa là giống hoặc tương tự. Tôi đang tưởng tượng nó? Hoặc có một sự khác biệt tinh tế giữa chúng?
Stewart

Câu trả lời:


201

Xóa một chức năng là một tính năng C ++ 11 :

Thành ngữ phổ biến của "cấm sao chép" bây giờ có thể được thể hiện trực tiếp:

class X {
    // ...
    X& operator=(const X&) = delete;  // Disallow copying
    X(const X&) = delete;
};

[...]

Cơ chế "xóa" có thể được sử dụng cho bất kỳ chức năng nào. Ví dụ: chúng ta có thể loại bỏ một chuyển đổi không mong muốn như thế này:

struct Z {
    // ...

    Z(long long);     // can initialize with an long long         
    Z(long) = delete; // but not anything less
};

3
Không phải là phương pháp truyền thống để "cấm sao chép" chỉ để làm cho copy-ctor và toán tử = "riêng tư?" Điều này đi xa hơn một chút và hướng dẫn trình biên dịch thậm chí không tạo ra các hàm. Nếu cả hai đều riêng tư và = xóa, việc sao chép có bị cấm không?
Reb.Cabin

8
@Reb, =deletelàm cho phương thức không thể truy cập được ngay cả từ các bối cảnh có thể thấy privatecác phương thức (tức là trong lớp và bạn bè của nó). Điều này loại bỏ bất kỳ sự không chắc chắn khi bạn đang đọc mã. @Prasoon, ví dụ thứ hai đó vẫn chỉ xóa các hàm tạo - thật tuyệt khi thấy một operator long ()ví dụ bị xóa .
Toby Speight

2
@ Reb.Cabin Sử dụng = deletetốt hơn so với sử dụng privatehoặc các cơ chế tương tự khác vì thông thường bạn muốn hàm bị cấm được khai báo rõ ràng và được xem xét để giải quyết quá tải, v.v., do đó nó có thể bị lỗi càng sớm càng tốt và cung cấp lỗi rõ ràng nhất cho người dùng. Bất kỳ giải pháp nào liên quan đến việc "che giấu" tuyên bố đều làm giảm hiệu ứng này.
Leushenko

1
Có một lý do đặc biệt để làm cho công cụ xây dựng sao chép công khai và áp dụng từ khóa xóa. Tại sao không để công cụ xây dựng riêng tư và áp dụng từ khóa?
Dohn Joe

81
  1. = 0có nghĩa là một hàm là thuần ảo và bạn không thể khởi tạo một đối tượng từ lớp này. Bạn cần xuất phát từ nó và thực hiện phương pháp này
  2. = deletecó nghĩa là trình biên dịch sẽ không tạo ra các hàm tạo đó cho bạn. AFAIK điều này chỉ được phép trên nhà xây dựng sao chép và toán tử gán. Nhưng tôi không quá giỏi ở tiêu chuẩn sắp tới.

4
Có một số cách sử dụng =deletecú pháp khác. Ví dụ: bạn có thể sử dụng nó để không cho phép rõ ràng một số loại chuyển đổi ngầm có thể diễn ra với cuộc gọi. Đối với điều này, bạn chỉ cần xóa các chức năng quá tải. Hãy xem trang Wikipedia trên C ++ 0x để biết thêm thông tin.
LiKao

Tôi sẽ làm điều đó ngay khi tôi tìm thấy một số. Đoán đây là lúc để bắt kịp với c ++ 0X
mkaes

Vâng, đá C ++ 0x. Tôi không thể chờ GCC 4.5+ trở nên phổ biến hơn, vì vậy tôi có thể bắt đầu sử dụng lambdas.
LiKao

5
Mô tả cho = deletekhông hoàn toàn chính xác. = deletecó thể được sử dụng cho bất kỳ chức năng nào, trong trường hợp đó, nó được đánh dấu rõ ràng là đã bị xóa và bất kỳ việc sử dụng nào cũng dẫn đến lỗi trình biên dịch. Đối với các hàm thành viên đặc biệt, điều này cũng có nghĩa cụ thể là chúng không được trình biên dịch tạo ra cho bạn, nhưng đó chỉ là kết quả của việc bị xóa chứ không phải = deletelà thực sự.
MicroVirus

28

Đoạn trích này từ Ngôn ngữ lập trình C ++ [Phiên bản thứ 4] - Cuốn sách Bjarne Stroustrup nói về mục đích thực sự đằng sau việc sử dụng =delete:

3.3.4 Hoạt động đàn áp

Sử dụng bản sao mặc định hoặc di chuyển cho một lớp trong hệ thống phân cấp thường là một thảm họa : chỉ được cung cấp một con trỏ đến một cơ sở, chúng tôi chỉ đơn giản là không biết các thành viên của lớp dẫn xuất có gì, vì vậy chúng tôi không thể biết cách sao chép chúng . Vì vậy, điều tốt nhất cần làm thường là xóa các hoạt động sao chép và di chuyển mặc định, nghĩa là loại bỏ các định nghĩa mặc định của hai thao tác đó:

class Shape {
public:
  Shape(const Shape&) =delete; // no copy operations
  Shape& operator=(const Shape&) =delete;

  Shape(Shape&&) =delete; // no move operations
  Shape& operator=(Shape&&) =delete;
  ˜Shape();
    // ...
};

Bây giờ một nỗ lực để sao chép một Shape sẽ được trình biên dịch bắt.

=deletechế này là chung, nghĩa là, nó có thể được sử dụng để triệt tiêu mọi hoạt động



5

Các tiêu chuẩn mã hóa mà tôi đã làm việc đã có những điều sau đây đối với hầu hết các khai báo lớp.

//  coding standard: disallow when not used
T(void)                  = delete; // default ctor    (1)
~T(void)                 = delete; // default dtor    (2)
T(const T&)              = delete; // copy ctor       (3)
T(const T&&)             = delete; // move ctor       (4)
T& operator= (const T&)  = delete; // copy assignment (5)
T& operator= (const T&&) = delete; // move assignment (6)

Nếu bạn sử dụng bất kỳ trong số 6, bạn chỉ cần nhận xét dòng tương ứng.

Ví dụ: lớp FizzBus chỉ yêu cầu dtor và do đó không sử dụng 5 cái còn lại.

//  coding standard: disallow when not used
FizzBuzz(void)                         = delete; // default ctor (1)
// ~FizzBuzz(void);                              // dtor         (2)
FizzBuzz(const FizzBuzz&)              = delete; // copy ctor    (3)
FizzBuzz& operator= (const FizzBuzz&)  = delete; // copy assig   (4)
FizzBuzz(const FizzBuzz&&)             = delete; // move ctor    (5)
FizzBuzz& operator= (const FizzBuzz&&) = delete; // move assign  (6)

Chúng tôi chỉ nhận xét 1 ở đây và cài đặt việc thực hiện nó ở nơi khác (có thể là tiêu chuẩn mã hóa gợi ý). 5 (6) khác không được phép xóa.

Bạn cũng có thể sử dụng '= xóa' để không cho phép quảng cáo ngầm các giá trị có kích thước khác nhau ... ví dụ

// disallow implicit promotions 
template <class T> operator T(void)              = delete;
template <class T> Vuint64& operator=  (const T) = delete;
template <class T> Vuint64& operator|= (const T) = delete;
template <class T> Vuint64& operator&= (const T) = delete;

3

= deletelà một tính năng giới thiệu trong C ++ 11. Theo =deletenó sẽ không được phép gọi chức năng đó.

Chi tiết.

Giả sử trong một lớp học.

Class ABC{
 Int d;
 Public:
  ABC& operator= (const ABC& obj) =delete
  {

  }
};

Trong khi gọi hàm này để gán obj, nó sẽ không được phép. Toán tử gán có nghĩa là sẽ hạn chế sao chép từ đối tượng này sang đối tượng khác.


2

Tiêu chuẩn C ++ 0x mới. Vui lòng xem phần 8.4.3 trong dự thảo làm việc của N3242


Whoa, dự thảo đó là hết hạn. Đây là vụ mới nhất (tính đến ngày 03 tháng tư năm 2011): open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
TonyK

Cảm ơn và cập nhật các liên kết. Rất hữu ích để có được dự thảo hiện tại. Phần / nội dung được tham chiếu là chính xác ngay cả trong dự thảo cũ vì vậy tôi không hiểu phiếu bầu giảm.
dubnde

1

Một chức năng bị xóa là ngầm định

(Phụ lục cho câu trả lời hiện có)

... Và một hàm bị xóa sẽ là khai báo đầu tiên của hàm (ngoại trừ việc xóa các chuyên môn rõ ràng của các mẫu hàm - việc xóa phải nằm ở phần khai báo đầu tiên của chuyên môn hóa), nghĩa là bạn không thể khai báo một hàm và sau đó xóa nó, nói, theo định nghĩa của nó cục bộ cho một đơn vị dịch thuật.

Trích dẫn [dcl.fct.def.delete] / 4 :

Một chức năng bị xóa là ngầm định. ( Lưu ý: Quy tắc một định nghĩa ( [basic.def.odr] ) áp dụng cho các định nghĩa đã xóa. - lưu ý cuối ] Định nghĩa đã xóa của hàm sẽ là khai báo đầu tiên của hàm hoặc, đối với chuyên môn hóa rõ ràng của mẫu hàm , tuyên bố đầu tiên của chuyên ngành đó. [Ví dụ:

struct sometype {
  sometype();
};
sometype::sometype() = delete;      // ill-formed; not first declaration

- ví dụ cuối )

Mẫu hàm chính với định nghĩa đã xóa có thể được chuyên biệt

Mặc dù một nguyên tắc chung là tránh các mẫu chức năng chuyên dụng vì các chuyên môn không tham gia vào bước đầu tiên của giải quyết quá tải, có thể tranh cãi một số bối cảnh có thể hữu ích. Ví dụ: khi sử dụng không quá tải mẫu hàm chính không có định nghĩa để khớp với tất cả các loại mà người ta không muốn chuyển đổi hoàn toàn sang quá tải chuyển đổi khớp bằng cách khác; tức là, để loại bỏ hoàn toàn một số đối sánh chuyển đổi ngầm định bằng cách chỉ thực hiện khớp chính xác loại trong việc chuyên môn hóa rõ ràng của mẫu hàm chính không được xác định, không quá tải.

Trước khi khái niệm chức năng bị xóa của C ++ 11, người ta có thể làm điều này bằng cách bỏ qua định nghĩa của mẫu hàm chính, nhưng điều này đã làm cho các lỗi tham chiếu không xác định được cho là không có ý nghĩa ngữ nghĩa nào từ tác giả của mẫu hàm chính (bỏ qua một cách có chủ ý ?). Thay vào đó, nếu chúng ta xóa rõ ràng mẫu chức năng chính, các thông báo lỗi trong trường hợp không tìm thấy chuyên môn rõ ràng phù hợp sẽ trở nên đẹp hơn nhiều và cũng cho thấy việc bỏ sót / xóa định nghĩa của mẫu chức năng chính là cố ý.

#include <iostream>
#include <string>

template< typename T >
void use_only_explicit_specializations(T t);

template<>
void use_only_explicit_specializations<int>(int t) {
    std::cout << "int: " << t;
}

int main()
{
    const int num = 42;
    const std::string str = "foo";
    use_only_explicit_specializations(num);  // int: 42
    //use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...
}

Tuy nhiên, thay vì chỉ đơn giản là bỏ qua một định nghĩa cho mẫu hàm chính ở trên, mang lại một lỗi tham chiếu không xác định tối nghĩa khi không có chuyên môn rõ ràng phù hợp, định nghĩa mẫu chính có thể bị xóa:

#include <iostream>
#include <string>

template< typename T >
void use_only_explicit_specializations(T t) = delete;

template<>
void use_only_explicit_specializations<int>(int t) {
    std::cout << "int: " << t;
}

int main()
{
    const int num = 42;
    const std::string str = "foo";
    use_only_explicit_specializations(num);  // int: 42
    use_only_explicit_specializations(str);
    /* error: call to deleted function 'use_only_explicit_specializations' 
       note: candidate function [with T = std::__1::basic_string<char>] has 
       been explicitly deleted
       void use_only_explicit_specializations(T t) = delete; */
}

Mang lại một thông báo lỗi dễ đọc hơn, trong đó mục đích xóa cũng được hiển thị rõ ràng (trong đó một lỗi tham chiếu không xác định có thể dẫn đến nhà phát triển nghĩ rằng đây là một lỗi không suy nghĩ).

Trở lại lý do tại sao chúng ta sẽ muốn sử dụng kỹ thuật này? Một lần nữa, các chuyên ngành rõ ràng có thể hữu ích để ngầm loại bỏ chuyển đổi tiềm ẩn.

#include <cstdint>
#include <iostream>

void warning_at_best(int8_t num) { 
    std::cout << "I better use -Werror and -pedantic... " << +num << "\n";
}

template< typename T >
void only_for_signed(T t) = delete;

template<>
void only_for_signed<int8_t>(int8_t t) {
    std::cout << "UB safe! 1 byte, " << +t << "\n";
}

template<>
void only_for_signed<int16_t>(int16_t t) {
    std::cout << "UB safe! 2 bytes, " << +t << "\n";
}

int main()
{
    const int8_t a = 42;
    const uint8_t b = 255U;
    const int16_t c = 255;
    const float d = 200.F;

    warning_at_best(a); // 42
    warning_at_best(b); // implementation-defined behaviour, no diagnostic required
    warning_at_best(c); // narrowing, -Wconstant-conversion warning
    warning_at_best(d); // undefined behaviour!

    only_for_signed(a);
    only_for_signed(c);

    //only_for_signed(b);  
    /* error: call to deleted function 'only_for_signed' 
       note: candidate function [with T = unsigned char] 
             has been explicitly deleted
       void only_for_signed(T t) = delete; */

    //only_for_signed(d);
    /* error: call to deleted function 'only_for_signed' 
       note: candidate function [with T = float] 
             has been explicitly deleted
       void only_for_signed(T t) = delete; */
}

0

Đây là điều mới trong các tiêu chuẩn C ++ 0x nơi bạn có thể xóa một chức năng được kế thừa.


11
Bạn có thể xóa bất kỳ chức năng. Ví dụ: void foo(int); template <class T> void foo(T) = delete;dừng tất cả các chuyển đổi ngầm. Chỉ các đối số intloại được chấp nhận, tất cả các đối số khác sẽ cố gắng khởi tạo chức năng "đã xóa".
ChúBens
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.