ทำไม C ++ 11 ทำให้" delete
d" ฟังก์ชั่นมีส่วนร่วมในการแก้ปัญหาเกิน ?
เหตุใดจึงมีประโยชน์ หรือกล่าวอีกนัยหนึ่งว่าเหตุใดจึงซ่อนไว้แทนที่จะถูกลบทั้งหมด
ทำไม C ++ 11 ทำให้" delete
d" ฟังก์ชั่นมีส่วนร่วมในการแก้ปัญหาเกิน ?
เหตุใดจึงมีประโยชน์ หรือกล่าวอีกนัยหนึ่งว่าเหตุใดจึงซ่อนไว้แทนที่จะถูกลบทั้งหมด
คำตอบ:
จุดประสงค์ครึ่งหนึ่งของ= delete
ไวยากรณ์คือเพื่อป้องกันไม่ให้ผู้คนเรียกใช้ฟังก์ชันบางอย่างด้วยพารามิเตอร์บางอย่าง ส่วนใหญ่เป็นการป้องกันการแปลงโดยนัยในสถานการณ์เฉพาะบางสถานการณ์ เพื่อที่จะห้ามไม่ให้มีการโอเวอร์โหลดโดยเฉพาะจะต้องมีส่วนร่วมในการแก้ปัญหาการโอเวอร์โหลด
คำตอบที่คุณอ้างถึงเป็นตัวอย่างที่สมบูรณ์แบบ:
struct onlydouble {
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
หากdelete
ลบฟังก์ชันออกทั้งหมดจะทำให้= delete
ไวยากรณ์เทียบเท่ากับสิ่งนี้:
struct onlydouble2 {
onlydouble2(double);
};
คุณสามารถทำได้:
onlydouble2 val(20);
นี่คือ C ++ ที่ถูกกฎหมาย คอมไพเลอร์จะดูตัวสร้างทั้งหมด ไม่มีใครใช้ประเภทจำนวนเต็มโดยตรง แต่หนึ่งในนั้นสามารถรับมันได้หลังจากการเปลี่ยนใจเลื่อมใสโดยปริยาย มันจะเรียกอย่างนั้น
onlydouble val(20);
นี่ไม่ใช่ C ++ ตามกฎหมาย คอมไพเลอร์จะดูตัวสร้างทั้งหมดรวมถึงตัวdelete
d ด้วย จะเห็นการจับคู่แบบตรงทั้งหมดผ่านstd::intmax_t
(ซึ่งจะตรงกับจำนวนเต็มลิเทอรัลใด ๆ ก็ตาม) ดังนั้นคอมไพลเลอร์จะเลือกและออกข้อผิดพลาดทันทีเนื่องจากเลือกdelete
ฟังก์ชัน d
= delete
หมายความว่า "ฉันห้ามสิ่งนี้" ไม่ใช่แค่ "สิ่งนี้ไม่มีอยู่จริง" มันเป็นคำสั่งที่แข็งแกร่งกว่ามาก
ฉันถามว่าทำไมมาตรฐาน C ++ พูดว่า = ลบหมายความว่า "ฉันห้ามสิ่งนี้" แทนที่จะเป็น "สิ่งนี้ไม่มีอยู่จริง"
เป็นเพราะเราไม่จำเป็นต้องใช้ไวยากรณ์พิเศษในการพูดว่า "สิ่งนี้ไม่มีอยู่จริง" เราเข้าใจสิ่งนี้โดยปริยายโดยการไม่ประกาศ "สิ่งนี้" ที่เป็นปัญหา "ฉันห้ามสิ่งนี้" แสดงถึงโครงสร้างที่ไม่สามารถทำได้หากไม่มีไวยากรณ์พิเศษ ดังนั้นเราจึงได้รับไวยากรณ์พิเศษที่จะพูดว่า "ฉันห้ามสิ่งนี้" ไม่ใช่อย่างอื่น
ฟังก์ชันเดียวที่คุณจะได้รับจากการมีไวยากรณ์ "สิ่งนี้ไม่มีอยู่จริง" อย่างชัดเจนคือการป้องกันไม่ให้ใครบางคนประกาศว่ามีอยู่ในภายหลัง และนั่นก็ไม่มีประโยชน์เพียงพอที่จะต้องใช้ไวยากรณ์ของตัวเอง
ไม่มีวิธีใดที่จะประกาศว่าไม่มีตัวสร้างการคัดลอกและการมีอยู่ของมันอาจทำให้เกิดความคลุมเครือที่ไร้สาระ
ตัวสร้างการคัดลอกเป็นฟังก์ชันสมาชิกพิเศษ ทุกชั้นเรียนจะมีตัวสร้างสำเนาเสมอ เช่นเดียวกับที่พวกเขามีตัวดำเนินการกำหนดสำเนาย้ายตัวสร้าง ฯลฯ เสมอ
มีฟังก์ชันเหล่านี้ คำถามคือจะเรียกพวกเขาว่าถูกกฎหมายหรือไม่ หากคุณพยายามบอกว่า= delete
นั่นหมายความว่าไม่มีอยู่จริงข้อกำหนดจะต้องอธิบายความหมายของฟังก์ชันที่ไม่มีอยู่ นี่ไม่ใช่แนวคิดที่สเปคจัดการ
หากคุณพยายามเรียกใช้ฟังก์ชันที่ยังไม่ได้ประกาศ / กำหนดคอมไพลเลอร์จะผิดพลาด แต่จะเกิดข้อผิดพลาดเนื่องจากตัวระบุที่ไม่ได้กำหนดไม่ใช่เนื่องจากข้อผิดพลาด "ไม่มีฟังก์ชัน" (แม้ว่าคอมไพเลอร์ของคุณจะรายงานในลักษณะนั้นก็ตาม) คอนสตรัคเตอร์ต่างๆล้วนถูกเรียกใช้ด้วยความละเอียดเกินพิกัดดังนั้น "การมีอยู่" ของมันจึงถูกจัดการในเรื่องนั้น
ในทุกกรณีจะมีฟังก์ชันที่ประกาศผ่านตัวระบุหรือตัวสร้าง / ตัวทำลาย (ประกาศผ่านตัวระบุเช่นกันเพียงแค่ตัวระบุประเภท) ตัวดำเนินการที่มากเกินไปจะซ่อนตัวระบุไว้ด้านหลังของน้ำตาลที่มีปฏิกิริยา แต่ก็ยังคงอยู่ที่นั่น
ข้อกำหนด C ++ ไม่สามารถจัดการกับแนวคิดของ "ฟังก์ชันที่ไม่มีอยู่จริง" สามารถจัดการกับการโอเวอร์โหลดที่ไม่ตรงกัน สามารถจัดการกับความคลุมเครือเกินพิกัด แต่มันไม่รู้เกี่ยวกับสิ่งที่ไม่มี ดังนั้นจึง= delete
ถูกกำหนดในแง่ของ "ความพยายามที่จะเรียกสิ่งนี้ว่าล้มเหลว" ที่มีประโยชน์มากกว่าคำว่า "แกล้งทำเป็นว่าฉันไม่เคยเขียนบรรทัดนี้" ที่มีประโยชน์น้อยกว่า
และอีกครั้งอ่านส่วนแรกอีกครั้ง คุณไม่สามารถทำได้โดย "ไม่มีฟังก์ชัน" นั่นเป็นอีกสาเหตุหนึ่งที่ทำให้มันถูกกำหนดแบบนั้น: เนื่องจากหนึ่งในกรณีการใช้งานหลักของ= delete
ไวยากรณ์คือการบังคับให้ผู้ใช้ใช้พารามิเตอร์บางประเภทเพื่อแคสต์อย่างชัดเจนและอื่น ๆ โดยทั่วไปเพื่อทำลายการแปลงประเภทโดยนัย
คำแนะนำของคุณจะไม่ทำเช่นนั้น
= delete
จะหมายถึง "ไม่มีสมาชิกคนนี้" ซึ่งหมายความว่าไม่สามารถมีส่วนร่วมในการแก้ไขปัญหาเกินพิกัดได้
= delete
หมายถึง "ไม่มีสมาชิกคนนี้" ตัวอย่างแรกที่ฉันโพสต์จะไม่สามารถป้องกันไม่ให้คนส่งจำนวนเต็มไปยังตัวonlydouble
สร้างของ เนื่องจากonlydouble
เกินนั้นจะถูกลบจะไม่อยู่ มันจะไม่เข้าร่วมในการแก้ปัญหาโอเวอร์โหลดดังนั้นจึงไม่ป้องกันไม่ให้คุณส่งผ่านจำนวนเต็ม ซึ่งเป็นครึ่งหนึ่งของจุดสำคัญของ= delete
ไวยากรณ์: เพื่อให้สามารถพูดได้ว่า "คุณไม่สามารถส่ง X ไปยังฟังก์ชันนี้โดยปริยายได้"
=delete
เลย? ท้ายที่สุดเราสามารถพูดว่า "ไม่สามารถคัดลอกได้" โดยทำสิ่งเดียวกันทุกประการนั่นคือการประกาศตัวสร้างการคัดลอก / การกำหนดเป็นส่วนตัว นอกจากนี้โปรดทราบว่าการประกาศสิ่งที่เป็นส่วนตัวไม่ได้ทำให้ไม่สามารถเรียกใช้ได้ รหัสภายในชั้นเรียนยังคงสามารถเรียกมันได้ = delete
ดังนั้นจึงไม่เหมือนกัน ไม่= delete
ไวยากรณ์ช่วยให้เราสามารถทำบางสิ่งที่ไม่สะดวกและไม่สามารถเข้าใจได้มาก่อนด้วยวิธีที่ชัดเจนและสมเหตุสมผลกว่ามาก
C ++ Working Draft 2012-11-02 ไม่ได้ให้เหตุผลเบื้องหลังกฎนี้เป็นเพียงตัวอย่างบางส่วน
8.4.3 นิยามที่ถูกลบ [dcl.fct.def.delete]
...
3 [ ตัวอย่าง : หนึ่งสามารถบังคับใช้การกำหนดค่าเริ่มต้นที่ไม่ใช่ค่าเริ่มต้นและการกำหนดค่าเริ่มต้นแบบไม่รวมกับ
struct onlydouble {
onlydouble() = delete; // OK, but redundant
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
- end example ]
[ ตัวอย่าง : หนึ่งสามารถป้องกันการใช้คลาสในนิพจน์ใหม่บางอย่างได้โดยใช้คำจำกัดความที่ถูกลบของตัวดำเนินการที่ผู้ใช้ประกาศใหม่สำหรับคลาสนั้น
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 ]
[ ตัวอย่าง : หนึ่งสามารถทำให้คลาสไม่สามารถคัดลอกได้เช่นย้ายอย่างเดียวโดยใช้คำจำกัดความที่ถูกลบของตัวสร้างการคัดลอกและตัวดำเนินการกำหนดสำเนาจากนั้นให้คำจำกัดความเริ่มต้นของตัวสร้างการย้ายและตัวดำเนินการกำหนดย้าย
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
- ตัวอย่างตอนท้าย ]