ความแตกต่างระหว่าง C ++ 03 throw () specifier C ++ 11 noexcept


100

มีความแตกต่างระหว่าง throw()และnoexceptอื่น ๆ นอกเหนือจากการตรวจสอบที่รันไทม์และรวบรวมเวลาตามลำดับ?

บทความ Wikipedia C ++ 11 นี้ชี้ให้เห็นว่าตัวระบุการโยน C ++ 03 เลิกใช้งานแล้ว
เหตุใดจึงมีnoexceptความสามารถเพียงพอที่จะครอบคลุมทุกสิ่งในเวลาคอมไพล์

[หมายเหตุ: ฉันตรวจสอบคำถามนี้และบทความนี้แล้ว แต่ไม่สามารถระบุเหตุผลที่ชัดเจนในการเลิกใช้งานได้]


7
Accodring นี้บทความที่ดีนอกจากนี้ยังnoexceptอาจเกิดขึ้นจากการตรวจสอบรันไทม์ แตกต่างที่สำคัญระหว่างพวกเขาที่ถูกทำลายnoexceptสาเหตุstd::terminateขณะที่หมดสาเหตุthrow std::unexpectedนอกจากนี้พฤติกรรมการคลี่คลายกองซ้อนที่แตกต่างกันเล็กน้อยในกรณีเหล่านี้
Fiktik

ไม่มีการตรวจสอบ "เวลารวบรวม" โดยมีข้อกำหนดข้อยกเว้นบางประการที่ตรวจสอบ "รันไทม์" ในรายการอื่น ๆ เป็นเพียงตำนานที่สร้างขึ้นโดยฝ่ายตรงข้ามของข้อกำหนดข้อยกเว้น C ++
curiousguy

คำตอบ:


129

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

noexceptถูกเพิ่มเข้ามาแทนที่จะลบตัวระบุการโยนทั้งหมดนอกเหนือจากthrow()เพราะnoexceptมีประสิทธิภาพมากกว่า noexceptสามารถมีพารามิเตอร์ที่คอมไพล์ - ไทม์แก้ไขเป็นบูลีน ถ้าบูลีนเป็นจริงnoexceptแท่ง ถ้าบูลีนเป็นเท็จแสดงnoexceptว่าไม่ติดและฟังก์ชันอาจโยน

ดังนั้นคุณสามารถทำสิ่งนี้:

struct<typename T>
{
  void CreateOtherClass() { T t{}; }
};

ไม่CreateOtherClassยกเว้นโยน? อาจเป็นไปได้หากตัวTสร้างเริ่มต้นสามารถทำได้ เราจะบอกยังไง? แบบนี้:

struct<typename T>
{
  void CreateOtherClass() noexcept(is_nothrow_default_constructible<T>::value) { T t{}; }
};

ดังนั้นCreateOtherClass()จะโยน iff ที่ตัวสร้างเริ่มต้นของประเภทที่กำหนดจะพ่น วิธีนี้แก้ไขปัญหาหลักอย่างหนึ่งของตัวระบุข้อยกเว้น: ไม่สามารถเผยแพร่ call stack ได้

คุณไม่สามารถทำได้ด้วยthrow().


+1 คำตอบที่เป็นประโยชน์สำหรับฉันแล้วล่ะ noexceptยังคงค้นหาคำตอบที่ว่าทำไมฉันจะต้องการใช้ ฉันไม่เคยใช้ตัวthrow()ระบุเลยและกำลังพยายามตรวจสอบว่าnoexceptให้ประโยชน์จริงหรือไม่ (นอกเหนือจากเอกสารประกอบการตรวจสอบคอมไพลเลอร์)
hmjd

เพิ่งเจอstackoverflow.com/questions/10787766/… ...
hmjd

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

2
@NicolBolas std::terminateโทรรันไทม์ ซึ่งเป็นวิธีที่แย่ที่สุด ! โค้ดสามารถแอบเข้าไปในรุ่นที่มีฟังก์ชันที่ทำเครื่องหมายไว้noexcept และตรวจพบการละเมิดที่รันไทม์ (หมายถึงที่ไซต์ของลูกค้า) ฉันหมายความว่าคอมไพเลอร์รับประกันว่าจะสร้างโค้ดที่ไม่ทิ้งข้อยกเว้นตั้งแต่แรก
Alex

2
@NicolBolas: อีกหนึ่งความแตกต่างที่น่าสังเกต หากมีการทำเครื่องหมายฟังก์ชันthrows()แล้วหากมีการโยนข้อยกเว้นสแต็กจะต้องถูกคลายออกไปจนถึงขอบเขตของฟังก์ชันนั้น (ดังนั้นตัวแปรอัตโนมัติทั้งหมดในฟังก์ชันจะถูกทำลาย) ที่จุดที่terminate()เรียกว่า (ผ่านunexpected()) หากมีการทำเครื่องหมายฟังก์ชันnoexceptแล้วหากมีข้อยกเว้นเกิดขึ้นระบบจะเรียกใช้การสิ้นสุด (การคลายสแต็กคือรายละเอียดที่กำหนดไว้สำหรับการนำไปใช้งาน)
Martin York

33

noexcept ไม่ได้ตรวจสอบในเวลาคอมไพล์

การนำไปใช้งานจะต้องไม่ปฏิเสธนิพจน์เพียงเพราะเมื่อดำเนินการแล้วมันจะพ่นหรืออาจทำให้เกิดข้อยกเว้นที่ฟังก์ชันที่มีไม่อนุญาต

เมื่อฟังก์ชันที่ถูกประกาศnoexceptหรือthrow()พยายามโยนข้อยกเว้นความแตกต่างเพียงอย่างเดียวคือการเรียกหนึ่งครั้งterminateและการเรียกอื่น ๆunexpectedและการจัดการข้อยกเว้นรูปแบบหลังถูกเลิกใช้อย่างมีประสิทธิภาพ


แต่ถ้าฟังก์ชันเสมือนมีthrow()/ การnoexceptตรวจสอบเวลาคอมไพล์ให้แน่ใจว่ามีโอเวอร์ไรเดอร์ด้วย
curiousguy

2

std::unexpected() ถูกเรียกโดยรันไทม์ C ++ เมื่อมีการละเมิดข้อกำหนดข้อยกเว้นแบบไดนามิก: ข้อยกเว้นจะถูกส่งออกจากฟังก์ชันที่มีข้อกำหนดข้อยกเว้นห้ามข้อยกเว้นประเภทนี้

std::unexpected() อาจเรียกได้โดยตรงจากโปรแกรม

ไม่ว่าในกรณีใดให้std::unexpectedเรียกไฟล์std::unexpected_handler. เริ่มต้นสายstd::unexpected_handlerstd::terminate

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