“ ค่าเริ่มต้น” หมายถึงอะไรหลังจากประกาศฟังก์ชัน 'คลาส'?


221

ฉันเคยเห็นdefaultใช้ถัดจากการประกาศฟังก์ชั่นในชั้นเรียน มันทำอะไร?

class C {
  C(const C&) = default;
  C(C&&) = default;
  C& operator=(const C&) & = default;
  C& operator=(C&&) & = default;
  virtual ~C() { }
};

26
"&" ที่นำหน้า "=" ในการประกาศโอเปอเรเตอร์การมอบหมายทำอะไร
dshin

6
@dshin นี้เป็นโทษวุฒิการศึกษาการทำงานของสมาชิก
Kane

คำตอบ:


249

มันเป็นคุณสมบัติC ++ 11ใหม่

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

คุณสามารถใช้= deleteเพื่อระบุว่าคุณไม่ต้องการให้คอมไพเลอร์สร้างฟังก์ชันนั้นโดยอัตโนมัติ

ด้วยการแนะนำของตัวสร้างการเคลื่อนย้ายและตัวดำเนินการกำหนดค่าการย้ายกฎสำหรับเมื่อตัวสร้างเวอร์ชันอัตโนมัติของตัวสร้าง destructors และตัวดำเนินการกำหนดได้กลายเป็นค่อนข้างซับซ้อน การใช้= defaultและ= deleteทำให้สิ่งต่าง ๆ ง่ายขึ้นโดยที่คุณไม่จำเป็นต้องจำกฎ: เพียงแค่พูดในสิ่งที่คุณต้องการจะเกิดขึ้น


17
= deleteแข็งแรง: หมายความว่าการใช้ฟังก์ชั่นนั้นเป็นสิ่งต้องห้ามแม้ว่าจะยังคงมีส่วนร่วมในการแก้ปัญหาการโอเวอร์โหลด
Deduplicator

2
แต่ถ้าเราต้องการใช้คอมไพเลอร์สร้างคำจำกัดความแล้วเราไม่ควรข้ามการเขียนฟังก์ชั่นนั้นแทนที่จะ "เขียนมันก่อนแล้วกำหนดมันให้เป็นค่าเริ่มต้น"?
Mayank Jindal

47

นี่คือคุณลักษณะ C ++ 0x ใหม่ที่บอกคอมไพเลอร์เพื่อสร้างเวอร์ชันเริ่มต้นของตัวสร้างหรือตัวดำเนินการที่ได้รับมอบหมายนั่นคืออันที่เพิ่งทำการคัดลอกหรือย้ายการกระทำสำหรับสมาชิกแต่ละคน สิ่งนี้มีประโยชน์เนื่องจากตัวสร้างการย้ายไม่ได้ถูกสร้างขึ้นตามค่าเริ่มต้นเสมอ (เช่นถ้าคุณมี destructor ที่กำหนดเอง) ซึ่งแตกต่างจากตัวสร้างสำเนา คอมไพเลอร์จัดการมันมากกว่าที่จะสะกดออกด้วยตัวคุณเองในแต่ละครั้ง

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

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

ดูรายละเอียดในมาตรา 12.8 ของมาตรฐาน


5
ถึงแม้ว่ามันจะไม่เพียง แต่สำหรับการก่อสร้างและการกำหนด แต่ยังนำไปใช้operator new/new[], operator delete/delete[]และทับถมของพวกเขา
เซบาสเตียนมัค

21

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

นอกจากนี้ทราบว่าในบางสถานการณ์มันเป็นไปไม่ได้ที่จะให้ผู้ใช้กำหนดสร้างเริ่มต้นที่จะทำงานเช่นเดียวกับคอมไพเลอร์สังเคราะห์ภายใต้ทั้งสองเริ่มต้นและค่าเริ่มต้น defaultให้คุณได้รับพฤติกรรมนั้นกลับมา


5
เกี่ยวกับย่อหน้าที่สองคุณสามารถยกตัวอย่างได้หรือไม่?
John Smith

11

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


1

ร่างมาตรฐาน 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

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

จากนั้นคำถามก็คือแน่นอนว่าฟังก์ชันใดที่สามารถประกาศได้โดยปริยายและเกิดขึ้นเมื่อใดซึ่งฉันได้อธิบายไว้ที่:

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.