สร้างข้อยกเว้นมาตรฐานด้วยอาร์กิวเมนต์ตัวชี้โมฆะและ postconditions ที่เป็นไปไม่ได้


9

พิจารณาโปรแกรมต่อไปนี้:

#include<stdexcept>
#include<iostream>

int main() {
    try {
        throw std::range_error(nullptr);
    } catch(const std::range_error&) {
        std::cout << "Caught!\n";
    }
}

GCC และ Clang ด้วยการโทร libstdc ++ std::terminateและยกเลิกโปรแกรมด้วยข้อความ

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_S_construct null not valid

เสียงดังกราวกับ libc ++ segfaults ในการสร้างข้อยกเว้น

ดูgodbolt

คอมไพเลอร์ทำงานตามมาตรฐานหรือไม่ ส่วนที่เกี่ยวข้องของมาตรฐาน[diagnostics.range.error] (C ++ 17 N4659) กล่าวว่าstd::range_errorมีconst char*Constructor Overload ซึ่งควรเป็นที่ต้องการมากกว่าconst std::string&Overload ส่วนนี้ยังไม่ได้ระบุเงื่อนไขใด ๆ บนตัวสร้างและระบุเพียง postcondition

postconditionsstrcmp(what(), what_­arg) == 0 :

postcondition นี้มักจะมีพฤติกรรมที่ไม่ได้กำหนดหากwhat_argเป็นตัวชี้โมฆะดังนั้นนี่หมายความว่าโปรแกรมของฉันยังมีพฤติกรรมที่ไม่ได้กำหนดและคอมไพเลอร์ทั้งคู่ทำตาม ถ้าไม่เราจะอ่าน postconditions ที่เป็นไปไม่ได้ในมาตรฐานได้อย่างไร?


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

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


แรงบันดาลใจจากคำถามนี้


ดูเหมือนstd :: range_errorได้รับอนุญาตให้เก็บสิ่งต่าง ๆ โดยอ้างอิงดังนั้นฉันจะไม่แปลกใจ การโทรwhat()เมื่อnullptrผ่านไปอาจทำให้เกิดปัญหา
Chipster

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

หาก nullptrผ่านไปแล้วฉันจะคิดว่าwhat()จะต้องทำการตรวจสอบอีกครั้งเพื่อให้ได้ค่า นั่นคือการยกเลิกการพิจารณาnullptrซึ่งเป็นปัญหาที่ดีที่สุดและบางอย่างที่จะผิดพลาดนั้นเลวร้ายที่สุด
Chipster

ฉันเห็นด้วยแม้ว่า มันจะต้องมีพฤติกรรมที่ไม่ได้กำหนด อย่างไรก็ตามการใส่คำตอบลงในนั้นอธิบายว่าทำไมเกินความสามารถของฉัน
Chipster

ผมคิดว่ามันมีจุดมุ่งหมายที่ว่ามันเป็นสิ่งที่จำเป็นที่จุดอาร์กิวเมนต์สตริง C ที่ถูกต้องตั้งแต่ใช้เพื่ออธิบายค่าของstrcmp what_argนั่นคือสิ่งที่เกี่ยวข้องจากส่วนมาตรฐานซี<cstring>กล่าวต่อไปซึ่งถูกอ้างถึงโดยสเปคของ แน่นอนว่าถ้อยคำอาจชัดเจนขึ้น
LF

คำตอบ:


0

จากเอกสาร :

เนื่องจากการคัดลอก std :: range_error ไม่ได้รับอนุญาตให้โยนข้อยกเว้นโดยทั่วไปข้อความนี้จะถูกเก็บไว้ภายในเป็นสตริงที่นับอ้างอิงที่จัดสรรแยกต่างหาก นี่คือสาเหตุที่ไม่มีคอนสตรัคเตอร์ที่ใช้ std :: string &&: มันจะต้องคัดลอกเนื้อหาอยู่ดี

นี่แสดงให้เห็นว่าทำไมคุณถึงได้รับ segfault api ทำตัวเหมือนเป็นสตริงจริง โดยทั่วไปใน cpp หากมีบางอย่างเป็นตัวเลือกจะมีคอนสตรัคเตอร์ / ฟังก์ชั่นโอเวอร์โหลดที่ไม่ใช้สิ่งที่ไม่ต้องการ ดังนั้นการส่งผ่านnullptrฟังก์ชั่นที่ไม่ได้บันทึกสิ่งที่เป็นทางเลือกจะเป็นพฤติกรรมที่ไม่ได้กำหนด โดยปกติแล้ว API จะไม่ใช้พอยน์เตอร์ที่มีข้อยกเว้นสำหรับสตริง C ดังนั้น IMHO จึงปลอดภัยที่จะถือว่าผ่าน nullptr สำหรับฟังก์ชันที่คาดว่าconst char *จะเป็นพฤติกรรมที่ไม่ได้กำหนด API ที่ใหม่กว่าอาจstd::string_viewเหมาะกับกรณีเหล่านี้

ปรับปรุง:

โดยปกติจะถือว่าการใช้ C ++ API เป็นตัวชี้เพื่อยอมรับค่า NULL อย่างไรก็ตามสตริง C เป็นกรณีพิเศษ จนกระทั่งstd::string_viewไม่มีวิธีที่ดีกว่าในการส่งผ่านอย่างมีประสิทธิภาพ โดยทั่วไปสำหรับการยอมรับ API const char *ควรสันนิษฐานว่ามันจะต้องเป็นสตริง C ที่ถูกต้อง เช่นตัวชี้ไปยังลำดับของchars ที่ลงท้ายด้วย '\ 0'

range_errorสามารถตรวจสอบว่าตัวชี้ไม่ได้nullptrแต่ไม่สามารถตรวจสอบได้หากตัวชี้นั้นจบลงด้วย '\ 0' ดังนั้นจึงเป็นการดีกว่าที่จะไม่ทำการตรวจสอบใด ๆ

ฉันไม่ทราบคำที่แน่นอนในมาตรฐาน แต่เงื่อนไขนี้อาจถูกสันนิษฐานโดยอัตโนมัติ


-2

สิ่งนี้กลับไปที่คำถามพื้นฐานหรือไม่ว่าตกลงเพื่อสร้าง std :: string จาก nullptr? และมันควรทำอย่างไร?

www.cplusplus.comพูดว่า

ถ้า s เป็นตัวชี้โมฆะถ้า n == npos หรือถ้าช่วงที่ระบุโดย [แรกสุด) ไม่ถูกต้องจะทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด

ดังนั้นเมื่อ

throw std::range_error(nullptr);

เรียกว่าการใช้งานพยายามทำสิ่งที่ต้องการ

store = std::make_shared<std::string>(nullptr);

ซึ่งไม่ได้กำหนด ซึ่งฉันจะพิจารณาข้อผิดพลาด (โดยไม่ต้องอ่านข้อความจริงในมาตรฐาน) แทนนักพัฒนา libery อาจทำสิ่งที่ต้องการ

if (what_arg)
  store = std::make_shared<std::string>(nullptr);

แต่จากนั้นตัวจับจะต้องตรวจสอบ nullptr what();ไม่เช่นนั้นมันก็จะพังตรงนั้น ดังนั้นstd::range_errorควรกำหนดสตริงว่างหรือ "(nullptr)" เหมือนภาษาอื่น ๆ


“ สิ่งนี้กลับไปที่คำถามพื้นฐานคือตกลงเพื่อสร้าง std :: string จาก nullptr หรือไม่” - ฉันไม่คิดว่ามันจะเป็นเช่นนั้น
Konrad Rudolph

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

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