ฉันเคยเห็นdefault
ใช้ถัดจากการประกาศฟังก์ชั่นในชั้นเรียน มันทำอะไร?
class C {
C(const C&) = default;
C(C&&) = default;
C& operator=(const C&) & = default;
C& operator=(C&&) & = default;
virtual ~C() { }
};
ฉันเคยเห็นdefault
ใช้ถัดจากการประกาศฟังก์ชั่นในชั้นเรียน มันทำอะไร?
class C {
C(const C&) = default;
C(C&&) = default;
C& operator=(const C&) & = default;
C& operator=(C&&) & = default;
virtual ~C() { }
};
คำตอบ:
มันเป็นคุณสมบัติC ++ 11ใหม่
หมายความว่าคุณต้องการใช้รุ่นที่คอมไพเลอร์สร้างขึ้นของฟังก์ชันนั้นดังนั้นคุณไม่จำเป็นต้องระบุเนื้อหา
คุณสามารถใช้= delete
เพื่อระบุว่าคุณไม่ต้องการให้คอมไพเลอร์สร้างฟังก์ชันนั้นโดยอัตโนมัติ
ด้วยการแนะนำของตัวสร้างการเคลื่อนย้ายและตัวดำเนินการกำหนดค่าการย้ายกฎสำหรับเมื่อตัวสร้างเวอร์ชันอัตโนมัติของตัวสร้าง destructors และตัวดำเนินการกำหนดได้กลายเป็นค่อนข้างซับซ้อน การใช้= default
และ= delete
ทำให้สิ่งต่าง ๆ ง่ายขึ้นโดยที่คุณไม่จำเป็นต้องจำกฎ: เพียงแค่พูดในสิ่งที่คุณต้องการจะเกิดขึ้น
= delete
แข็งแรง: หมายความว่าการใช้ฟังก์ชั่นนั้นเป็นสิ่งต้องห้ามแม้ว่าจะยังคงมีส่วนร่วมในการแก้ปัญหาการโอเวอร์โหลด
นี่คือคุณลักษณะ C ++ 0x ใหม่ที่บอกคอมไพเลอร์เพื่อสร้างเวอร์ชันเริ่มต้นของตัวสร้างหรือตัวดำเนินการที่ได้รับมอบหมายนั่นคืออันที่เพิ่งทำการคัดลอกหรือย้ายการกระทำสำหรับสมาชิกแต่ละคน สิ่งนี้มีประโยชน์เนื่องจากตัวสร้างการย้ายไม่ได้ถูกสร้างขึ้นตามค่าเริ่มต้นเสมอ (เช่นถ้าคุณมี destructor ที่กำหนดเอง) ซึ่งแตกต่างจากตัวสร้างสำเนา คอมไพเลอร์จัดการมันมากกว่าที่จะสะกดออกด้วยตัวคุณเองในแต่ละครั้ง
นอกจากนี้ให้สังเกตว่าตัวสร้างเริ่มต้นจะไม่ถูกสร้างขึ้นหากคุณให้ตัวสร้างที่ไม่ใช่ค่าเริ่มต้นอื่น ๆ หากคุณยังต้องการคอนสตรัคเตอร์เริ่มต้นด้วยคุณสามารถใช้ไวยากรณ์นี้เพื่อให้คอมไพเลอร์สร้างขึ้นมา
เป็นกรณีการใช้งานอื่นมีหลายสถานการณ์ที่ตัวสร้างสำเนาจะไม่ถูกสร้างขึ้นโดยปริยาย (เช่นถ้าคุณให้ตัวสร้างการย้ายแบบกำหนดเอง) หากคุณยังต้องการรุ่นเริ่มต้นคุณสามารถขอได้ด้วยไวยากรณ์นี้
ดูรายละเอียดในมาตรา 12.8 ของมาตรฐาน
operator new/new[]
, operator delete/delete[]
และทับถมของพวกเขา
มันเป็นของใหม่ใน C ++ 11 ดูที่นี่ มันจะค่อนข้างมีประโยชน์ถ้าคุณได้กำหนดตัวสร้างหนึ่ง แต่ต้องการใช้ค่าเริ่มต้นสำหรับคนอื่น ๆ Pre-C ++ 11 คุณจะต้องกำหนดตัวสร้างทั้งหมดเมื่อคุณได้กำหนดไว้แล้วถึงแม้ว่าพวกเขาจะเทียบเท่ากับค่าเริ่มต้น
นอกจากนี้ทราบว่าในบางสถานการณ์มันเป็นไปไม่ได้ที่จะให้ผู้ใช้กำหนดสร้างเริ่มต้นที่จะทำงานเช่นเดียวกับคอมไพเลอร์สังเคราะห์ภายใต้ทั้งสองเริ่มต้นและค่าเริ่มต้น default
ให้คุณได้รับพฤติกรรมนั้นกลับมา
อีกกรณีการใช้งานที่ฉันไม่เห็นกล่าวถึงในคำตอบเหล่านี้คือมันช่วยให้คุณสามารถเปลี่ยนการมองเห็นของตัวสร้าง ตัวอย่างเช่นคุณอาจต้องการให้เพื่อนในชั้นเรียนสามารถเข้าถึงตัวสร้างสำเนาได้ แต่คุณไม่ต้องการให้มีการเปิดเผยต่อสาธารณะ
ร่างมาตรฐาน C ++ 17 N4659
https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 11.4.2 "ฟังก์ชั่นที่ผิดนัดอย่างชัดเจน":
1 นิยามฟังก์ชั่นของแบบฟอร์ม:
attribute-specifier-seq opt decl-specifier-seq opt declarator virt-specifier-seq opt = default ;
เรียกว่าคำจำกัดความที่ผิดนัดอย่างชัดเจน ฟังก์ชั่นที่ผิดนัดต้องชัดเจน
(1.1) - เป็นฟังก์ชั่นสมาชิกพิเศษ
(1.2) - มีประเภทฟังก์ชั่นที่ประกาศเหมือนกัน (ยกเว้นอาจแตกต่างจากตัวระบุคุณสมบัติและยกเว้นในกรณีของตัวสร้างสำเนาหรือตัวดำเนินการกำหนดค่าคัดลอกชนิดพารามิเตอร์อาจเป็น“ อ้างอิงถึง non-const T” โดย T คือ ชื่อคลาสของฟังก์ชันของสมาชิก) ราวกับว่ามันถูกประกาศโดยปริยายและ
(1.3) - ไม่มีอาร์กิวเมนต์เริ่มต้น
2 ฟังก์ชั่นเริ่มต้นอย่างชัดเจนที่ไม่ได้กำหนดว่าถูกลบอาจถูกประกาศ constexpr เฉพาะในกรณีที่มันจะได้รับการประกาศโดยนัยว่าเป็น constexpr หากฟังก์ชั่นเป็นค่าเริ่มต้นอย่างชัดเจนในการประกาศครั้งแรกของมันก็ถือว่าโดยปริยายเป็น constexpr ถ้าการประกาศโดยนัยจะเป็น
3 ถ้าฟังก์ชั่นที่ผิดนัดอย่างชัดเจนถูกประกาศด้วย noexcept-specifier ที่ไม่ได้สร้างข้อกำหนดข้อยกเว้นเช่นเดียวกับการประกาศโดยนัย (18.4) จากนั้น
(3.1) - หากฟังก์ชั่นนั้นผิดนัดในการประกาศครั้งแรกฟังก์ชันจะถูกกำหนดเป็นลบ
(3.2) - ไม่เช่นนั้นโปรแกรมจะมีรูปแบบไม่ดี
4 [ตัวอย่าง:
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
- ตัวอย่างท้าย]
5 ฟังก์ชั่นที่ผิดนัดอย่างชัดเจนและฟังก์ชั่นที่ประกาศโดยปริยายจะเรียกว่าฟังก์ชั่นเริ่มต้นและการดำเนินการจะต้องให้คำจำกัดความโดยนัยสำหรับพวกเขา (15.1 15.4, 15.8) ซึ่งอาจหมายถึงการกำหนดว่า ฟังก์ชั่นมีให้โดยผู้ใช้หากมีการประกาศโดยผู้ใช้และไม่ได้กำหนดค่าเริ่มต้นหรือลบอย่างชัดเจนในการประกาศครั้งแรก ฟังก์ชั่นที่ผู้ใช้กำหนดอย่างชัดเจนโดยปริยาย (เช่นการกำหนดค่าเริ่มต้นอย่างชัดเจนหลังจากการประกาศครั้งแรก) ถูกกำหนด ณ จุดที่มีการผิดนัดอย่างชัดเจน ถ้าฟังก์ชั่นดังกล่าวมีการกำหนดโดยนัยว่าถูกลบไปแล้วโปรแกรมนั้นจะมีรูปแบบไม่ดี [หมายเหตุ: การประกาศฟังก์ชันตามค่าเริ่มต้นหลังจากการประกาศครั้งแรกสามารถให้การดำเนินการที่มีประสิทธิภาพและคำจำกัดความรัดกุมในขณะที่เปิดใช้งานอินเทอร์เฟซแบบไบนารี่ที่เสถียรกับฐานรหัสที่พัฒนาขึ้น - บันทึกท้าย]
6 [ตัวอย่าง:
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
- ตัวอย่างท้าย]
จากนั้นคำถามก็คือแน่นอนว่าฟังก์ชันใดที่สามารถประกาศได้โดยปริยายและเกิดขึ้นเมื่อใดซึ่งฉันได้อธิบายไว้ที่: