ความหมายของ = ลบหลังจากประกาศฟังก์ชัน


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

อะไร= deleteหมายถึงในบริบทที่?

จะมีผู้ใด "ปรับเปลี่ยน" อื่น ๆ ( = 0และ= delete)?


23
@Blindy มันจะเป็นมาตรฐานใน C ++ 0x คือเร็ว ๆ นี้
Konrad Rudolph

1
ฉันยืนถูกต้องฉันพลาดคุณลักษณะ C ++ 0x นี้ ฉันคิดว่ามัน#defineเป็น la Qt ที่ประเมินเป็น 0 แล้วประกาศฟังก์ชั่นที่ซ่อนอยู่หรือบางสิ่งบางอย่าง
Blindy

ฉันจำคำหลัก 'ปิดใช้งาน' ซึ่งหมายถึงสิ่งเดียวกันหรือคล้ายกัน ฉันจินตนาการไว้หรือไม่ หรือมีความแตกต่างเล็กน้อยระหว่างพวกเขา?
สจ๊วต

คำตอบ:


201

การลบฟังก์ชันคือคุณลักษณะ C ++ 11 :

สำนวนทั่วไปของ "ห้ามคัดลอก" สามารถแสดงได้โดยตรง:

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

[ ... ]

กลไก "ลบ" สามารถใช้ได้กับทุกฟังก์ชั่น ตัวอย่างเช่นเราสามารถกำจัดการแปลงที่ไม่ต้องการเช่นนี้:

struct Z {
    // ...

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

3
ไม่ใช่วิธีการดั้งเดิมในการ "ห้ามคัดลอก" เพื่อทำสำเนา ctor และโอเปอเรเตอร์ = "ส่วนตัว" สิ่งนี้จะเพิ่มขึ้นอีกเล็กน้อยและสั่งให้คอมไพเลอร์ไม่แม้แต่สร้างฟังก์ชั่น หากทั้งคู่เป็นส่วนตัวและ = ลบจะคัดลอกสิ่งต้องห้ามเป็นสองเท่าหรือไม่?
Reb.Cabin

8
@Reb =deleteทำให้วิธีไม่สามารถเข้าถึงได้แม้จากบริบทที่สามารถดูprivateวิธีการ (เช่นภายในชั้นเรียนและเพื่อน ๆ ) สิ่งนี้จะลบความไม่แน่นอนเมื่อคุณอ่านรหัส @rasoon ตัวอย่างที่สองนั้นยังคงเป็นเพียงการลบ constructors - มันเป็นการดีที่จะเห็นการลบoperator long ()ตัวอย่างเช่น
Toby Speight

2
@ Reb.Cabin การใช้= deleteดีกว่าการใช้privateหรือกลไกอื่น ๆ ที่คล้ายกันเพราะโดยปกติแล้วคุณต้องการให้ฟังก์ชั่นต้องห้ามถูกประกาศอย่างชัดแจ้งและพิจารณาถึงความละเอียดเกินพิกัด ฯลฯ เพื่อที่ว่ามันจะล้มเหลวเร็วที่สุดเท่าที่จะเป็นไปได้ โซลูชันใด ๆ ที่เกี่ยวข้องกับ "การซ่อน" การประกาศจะลดผลกระทบนี้
Leushenko

1
มีเหตุผลพิเศษหรือไม่ที่จะทำให้ตัวสร้างสำเนาเป็นแบบสาธารณะและใช้คำหลักลบ ทำไมไม่ปล่อยให้คอนสตรัคเตอร์เป็นส่วนตัวและใช้คำค้นหา?
Dohn Joe

81
  1. = 0หมายความว่าฟังก์ชั่นนั้นเป็นเสมือนจริงและคุณไม่สามารถสร้างอินสแตนซ์ของวัตถุจากคลาสนี้ได้ คุณต้องได้รับจากมันและใช้วิธีนี้
  2. = deleteหมายความว่าคอมไพเลอร์จะไม่สร้างตัวสร้างเหล่านั้นให้คุณ AFAIK ใช้ได้เฉพาะกับตัวสร้างสำเนาและตัวดำเนินการกำหนดค่า แต่ฉันไม่เก่งในมาตรฐานที่กำลังจะมาถึง

4
มีการใช้งานอื่น ๆ ของ=deleteไวยากรณ์ ตัวอย่างเช่นคุณสามารถใช้เพื่อยกเลิกการแปลงโดยนัยบางอย่างที่อาจเกิดขึ้นกับการโทร สำหรับสิ่งนี้คุณเพียงแค่ลบฟังก์ชั่นที่โอเวอร์โหลด ดูหน้า Wikipedia บน C ++ 0x เพื่อดูข้อมูลเพิ่มเติม
LiKao

ฉันจะทำอย่างนั้นทันทีที่พบ คิดว่าถึงเวลาที่ต้องติดต่อกับ c ++ 0X
mkaes

ใช่หิน C ++ 0x ฉันไม่สามารถรอให้ GCC 4.5+ เป็นเรื่องธรรมดาได้มากขึ้นดังนั้นฉันจึงสามารถเริ่มใช้ lambdas ได้
LiKao

5
คำอธิบายสำหรับ= deleteไม่ถูกต้องทั้งหมด = deleteสามารถใช้สำหรับฟังก์ชั่นใด ๆ ซึ่งในกรณีนี้มันถูกทำเครื่องหมายอย่างชัดเจนว่าถูกลบและผลการใช้งานใด ๆ ในข้อผิดพลาดของคอมไพเลอร์ สำหรับฟังก์ชั่นพิเศษของสมาชิกสิ่งนี้หมายถึงโดยเฉพาะอย่างยิ่งว่ามันไม่ได้ถูกสร้างขึ้นสำหรับคุณโดยคอมไพเลอร์ แต่นั่นเป็นเพียงผลลัพธ์ของการลบและไม่ใช่สิ่งที่= deleteแท้จริง
MicroVirus

28

ข้อความที่ตัดตอนมาจากภาษาการเขียนโปรแกรม C ++ [ฉบับที่ 4] -หนังสือBjarne Stroustrupพูดถึงจุดประสงค์ที่แท้จริงของการใช้=delete:

3.3.4 การระงับการปฏิบัติงาน

เริ่มต้นการใช้สำเนาหรือย้ายสำหรับการเรียนในลำดับชั้นที่โดยปกติจะเป็นภัยพิบัติ : รับเพียงตัวชี้ไปยังฐานที่เราก็ไม่ทราบว่าสิ่งที่สมาชิกชั้นเรียนมาได้ดังนั้นเราจึงไม่สามารถรู้วิธีการคัดลอก ดังนั้นสิ่งที่ดีที่สุดที่ต้องทำคือการลบการคัดลอกและย้ายเริ่มต้นนั่นคือการกำจัดคำจำกัดความเริ่มต้นของการดำเนินการทั้งสอง:

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();
    // ...
};

ตอนนี้ความพยายามในการคัดลอกรูปร่างจะถูกรวบรวมโดยคอมไพเลอร์

=deleteกลไกทั่วไปว่าคือมันสามารถนำมาใช้ในการปราบปรามการดำเนินการใด ๆ


10

จะมีผู้ใด "ปรับเปลี่ยน" อื่น ๆ ( = 0และ= delete)?

=defaultเพราะมันปรากฏไม่มีใครตอบคำถามนี้ฉันควรจะพูดถึงว่านอกจากนี้ยังมี

https://docs.microsoft.com/en-us/cpp/cpp/explicitly-defaulted-and-deleted-functions#explicitly-defaulted-functions


1
ข้ากำลังมองหาสิ่งนี้อยู่
Coder กระตือรือร้น

5

มาตรฐานการเข้ารหัสที่ฉันทำงานด้วยมีดังต่อไปนี้สำหรับการประกาศในชั้นเรียนส่วนใหญ่

//  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)

หากคุณใช้ข้อใดข้อหนึ่งใน 6 ข้อนี้คุณก็สามารถคอมเม้นท์บรรทัดที่เกี่ยวข้องได้

ตัวอย่าง: คลาส FizzBus ต้องการเฉพาะ dtor และดังนั้นอย่าใช้ 5 ตัวอื่น

//  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)

เราแสดงความคิดเห็นเพียง 1 ที่นี่และติดตั้งการใช้งานที่อื่น (อาจเป็นที่มาตรฐานการเข้ารหัสแนะนำ) อีก 5 (จาก 6) ไม่อนุญาตให้ลบ

นอกจากนี้คุณยังสามารถใช้ '= ลบ' เพื่อไม่อนุญาตการประชาสัมพันธ์โดยนัยที่มีขนาดต่างกัน ... ตัวอย่าง

// 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

= deleteเป็นคุณสมบัติที่แนะนำใน C ++ 11 ตาม=deleteมันจะไม่ได้รับอนุญาตให้เรียกใช้ฟังก์ชันนั้น

ในรายละเอียด.

สมมติว่าในชั้นเรียน

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

  }
};

ในขณะที่เรียกใช้ฟังก์ชันนี้เพื่อรับการกำหนด obj จะไม่ได้รับอนุญาต หมายถึงผู้ประกอบการที่ได้รับมอบหมายกำลังจะ จำกัด การคัดลอกจากวัตถุหนึ่งไปยังอีก


2

ใหม่มาตรฐาน C ++ 0x โปรดดูหัวข้อ8.4.3 ในร่างการทำงานของ N3242


อ๊ะร่างนั้นล้าสมัยแล้ว นี่คือล่าสุด (ณ วันที่ 3 เมษายน 2011): open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
TonyK

ขอบคุณและอัพเดทลิงค์ มีประโยชน์มากในการรับร่างปัจจุบัน ส่วน / เนื้อหาที่อ้างอิงนั้นถูกต้องแม้จะเป็นร่างแบบเก่าดังนั้นฉันจึงไม่เข้าใจการลงคะแนนเสียง
dubnde

1

ฟังก์ชั่นที่ถูกลบเป็นแบบอินไลน์โดยนัย

(ภาคผนวกของคำตอบที่มีอยู่)

... และฟังก์ชั่นที่ถูกลบจะเป็นการประกาศครั้งแรกของฟังก์ชั่น (ยกเว้นการลบความเชี่ยวชาญเฉพาะด้านของแม่แบบฟังก์ชั่น - การลบควรอยู่ที่การประกาศครั้งแรกของความเชี่ยวชาญ) ซึ่งหมายความว่าคุณไม่สามารถประกาศฟังก์ชั่น ที่ definition แบบโลคัลเป็นหน่วยการแปล

อ้างถึง[dcl.fct.def.delete] / 4 :

ฟังก์ชั่นที่ถูกลบเป็นแบบอินไลน์โดยนัย ( หมายเหตุ:กฎหนึ่งนิยาม ( [basic.def.odr] ) นำไปใช้กับคำจำกัดความที่ถูกลบ - หมายเหตุท้าย ] คำนิยามที่ถูกลบของฟังก์ชั่นจะเป็นการประกาศครั้งแรกของฟังก์ชั่นหรือสำหรับความเชี่ยวชาญเฉพาะของแม่แบบฟังก์ชั่น การประกาศครั้งแรกของความเชี่ยวชาญนั้น [ตัวอย่าง:

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

- ตัวอย่างท้าย )

แม่แบบฟังก์ชั่นหลักที่มีความหมายที่ถูกลบสามารถเฉพาะ

แม้ว่ากฎทั่วไปของหัวแม่มือคือการหลีกเลี่ยงแม่แบบฟังก์ชั่นที่เชี่ยวชาญเป็นพิเศษไม่ได้มีส่วนร่วมในขั้นตอนแรกของการแก้ปัญหาเกินพิกัดที่มีเนื้อหาบางบริบทที่สามารถเป็นประโยชน์ เช่นเมื่อใช้เท็มเพลตฟังก์ชั่นหลักที่ไม่มีภาระมากเกินไปโดยไม่มีคำจำกัดความเพื่อจับคู่ทุกประเภทที่เราไม่ต้องการแปลงให้เป็นโอเวอร์โหลดที่ตรงกันโดยการแปลง กล่าวคือในการลบจำนวนของการจับคู่การแปลงโดยนัยโดยการใช้การจับคู่ประเภทที่แน่นอนเท่านั้นในการระบุเฉพาะอย่างชัดเจนของเท็มเพลตฟังก์ชันหลักที่ไม่ได้กำหนดไว้และไม่โอเวอร์โหลด

ก่อนที่แนวคิดเกี่ยวกับฟังก์ชันที่ถูกลบของ C ++ 11 เราสามารถทำได้โดยเพียงแค่ละเว้นคำจำกัดความของเท็มเพลตฟังก์ชันหลัก แต่สิ่งนี้ทำให้ข้อผิดพลาดการอ้างอิงที่ไม่ได้กำหนดชัดเจนซึ่งเนื้อหาไม่ได้ให้ความหมาย ?) หากเราลบเท็มเพลตฟังก์ชั่นหลักอย่างชัดเจนแทนข้อความแสดงข้อผิดพลาดในกรณีที่ไม่พบความเชี่ยวชาญเฉพาะทางที่เหมาะสมกลายเป็นดีกว่ามากและยังแสดงให้เห็นว่าการละเว้น / การลบคำจำกัดความของเทมเพลตฟังก์ชันหลักนั้น

#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< ...
}

อย่างไรก็ตามแทนที่จะละเว้นการนิยามสำหรับเท็มเพลตฟังก์ชันหลักด้านบนทำให้เกิดข้อผิดพลาดการอ้างอิงที่ไม่ชัดเจนซึ่งไม่ชัดเจนเมื่อไม่มีความเชี่ยวชาญที่ชัดเจนตรงกับนิยามเท็มเพลตหลักสามารถลบได้:

#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; */
}

ผลผลิตข้อความเพิ่มเติมอ่านได้มากขึ้นข้อผิดพลาดที่เจตนาการลบยังสามารถมองเห็นได้อย่างชัดเจน (ในกรณีที่การอ้างอิงที่ไม่ได้กำหนดข้อผิดพลาดที่อาจนำไปสู่การพัฒนาที่คิดนี้ผิดพลาด unthoughtful)

กลับไปที่สาเหตุที่เราเคยต้องการใช้เทคนิคนี้หรือไม่ อีกครั้งเฉพาะด้านอย่างชัดเจนอาจจะมีประโยชน์ในการปริยายลบแปลงโดยปริยาย

#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

นี่คือสิ่งใหม่ในมาตรฐาน C ++ 0x ซึ่งคุณสามารถลบฟังก์ชันที่สืบทอดมาได้


11
คุณสามารถลบฟังก์ชั่นใด ๆ เช่นvoid foo(int); template <class T> void foo(T) = delete;หยุดการแปลงโดยนัยทั้งหมด ยอมรับเฉพาะอาร์กิวเมนต์intประเภทเท่านั้นส่วนอื่น ๆ ทั้งหมดจะพยายามยกตัวอย่างฟังก์ชั่น "ลบ"
ลุง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.