ใน C ++ ถ้า throw คือนิพจน์ประเภทของมันคืออะไร?


115

ฉันเลือกสิ่งนี้ในการโจมตีสั้น ๆ ของฉันเพื่อ reddit:

http://www.smallshire.org.uk/sufficientlysmall/2009/07/31/in-c-throw-is-an-expression/

โดยทั่วไปผู้เขียนชี้ให้เห็นว่าใน C ++:

throw "error"

คือการแสดงออก นี่เป็นการสะกดที่ชัดเจนในมาตรฐาน C ++ ทั้งในข้อความหลักและไวยากรณ์ อย่างไรก็ตามสิ่งที่ไม่ชัดเจน (สำหรับฉันอย่างน้อยที่สุด) คือประเภทของนิพจน์คืออะไร? ฉันเดาว่า " void" แต่การทดลองเล็กน้อยกับ g ++ 4.4.0 และ Comeau ให้รหัสนี้:

    void f() {
    }

    struct S {};

    int main() {
        int x = 1;
        const char * p1 = x == 1 ? "foo" : throw S();  // 1
        const char * p2 = x == 1 ? "foo" : f();        // 2
    }

คอมไพลเลอร์ไม่มีปัญหากับ // 1 แต่ถูกกำหนดไว้ที่ // 2 เนื่องจากประเภทในตัวดำเนินการเงื่อนไขแตกต่างกัน ดังนั้นประเภทของthrowนิพจน์ดูเหมือนจะไม่เป็นโมฆะ

แล้วมันคืออะไร?

หากคุณตอบได้โปรดสำรองข้อความของคุณด้วยคำพูดจาก Standard


สิ่งนี้ดูเหมือนจะไม่เกี่ยวกับประเภทของนิพจน์การโยนมากนักเนื่องจากตัวดำเนินการตามเงื่อนไขเกี่ยวข้องกับนิพจน์การโยนซึ่งเป็นสิ่งที่ฉันไม่รู้มาก่อนในวันนี้อย่างแน่นอน ขอบคุณทุกคนที่ตอบกลับ แต่โดยเฉพาะอย่างยิ่งกับ David Thornley

c++  throw 

10
+1 คำถามสุดเจ๋ง และวิธีทดสอบที่ชาญฉลาด
Jeremy Powell

1
ลิงค์นั้นดูเหมือนจะค่อนข้างชัดเจนว่าประเภทถูกกำหนดโดยคอมไพเลอร์ให้เป็นอะไรก็ตามที่มันต้องการ
Draemon

ฉันคิดว่าบทความที่เชื่อมโยงนั้นได้รับการอัปเดตตั้งแต่ที่ฉันดูและฉันแน่ใจว่าเป็นเช่นนั้นจริง อย่างไรก็ตามฉันไม่พบในมาตรฐาน

และอาจจะไม่ - double d = โยน "foo"; เป็นข้อผิดพลาดกับ g + = (ยังไม่ได้ทดสอบด้วย comeau)

+1 ฉันอยากรู้คำตอบ
AraK

คำตอบ:


96

ตามมาตรฐาน 5.16 ย่อหน้าที่ 2 จุดแรก "ตัวถูกดำเนินการที่สองหรือที่สาม (แต่ไม่ใช่ทั้งสองอย่าง) เป็นนิพจน์การโยน (15.1) ผลลัพธ์เป็นแบบอื่นและเป็นค่า rvalue" ดังนั้นตัวดำเนินการตามเงื่อนไขจึงไม่สนใจว่าประเภทของ Throw-expression คืออะไร แต่จะใช้ประเภทอื่น

ในความเป็นจริง 15.1 ย่อหน้าที่ 1 กล่าวอย่างชัดเจนว่า "การแสดงออกของการโยนเป็นโมฆะประเภทหนึ่ง"


9
ตกลง - ฉันคิดว่าเรามีผู้ชนะ

สังเกตว่า Throw-expression คือการกำหนดนิพจน์ ดังนั้นจึงเป็นข้อผิดพลาดทางไวยากรณ์ซึ่งเป็นอาร์กิวเมนต์ของตัวดำเนินการส่วนใหญ่ เห็นได้ชัดว่าคุณซ่อนไว้ในวงเล็บได้ แต่ถ้าไม่ถูกละเว้น (เช่นอาร์กิวเมนต์แรกของตัวดำเนินการในตัว) แสดงว่าเป็นข้อผิดพลาดประเภท
AProgrammer

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

31

"การแสดงออกของการโยนเป็นโมฆะประเภท"

ISO14882 มาตรา 15


จากนั้นทั้ง g ++ และ Comeau จะไม่ได้รับข้อผิดพลาดในกรณี // 1 ของฉัน?

2
@Neil ไม่ใช่เพราะตาม C ++ / 5.16 / 2 ตัวถูกดำเนินการที่สองและสามของตัวดำเนินการตามเงื่อนไขสามารถเป็นประเภทvoid
mloskot

13

จาก [expr.cond.2] (ตัวดำเนินการตามเงื่อนไข?:):

หากตัวถูกดำเนินการที่สองหรือตัวที่สามมีประเภท (อาจเป็น cv-quali fi ed) เป็นโมฆะดังนั้นการแปลงมาตรฐาน lvalue-to-rvalue, array-to-pointer และ function-to-pointer จะดำเนินการกับตัวถูกดำเนินการที่สองและสามและ อย่างใดอย่างหนึ่งต่อไปนี้จะถือ:

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

- ทั้งตัวถูกดำเนินการที่สองและตัวที่สามมีประเภทเป็นโมฆะ ผลลัพธ์เป็นโมฆะประเภทและเป็นค่า rvalue [หมายเหตุ: กรณีนี้รวมถึงกรณีที่ตัวถูกดำเนินการทั้งสองเป็นนิพจน์โยน - หมายเหตุ]

ดังนั้นกับ//1คุณในกรณีแรก//2คุณกำลังละเมิด "ข้อใดข้อหนึ่งต่อไปนี้จะถือ" เนื่องจากไม่มีผู้ใดกระทำในกรณีนั้น


3

คุณสามารถมีเครื่องพิมพ์แบบพ่นออกมาให้คุณได้ :

template<typename T>
struct PrintType;

int main()
{
    PrintType<decltype(throw "error")> a; 
}

โดยทั่วไปการขาดการนำไปใช้PrintTypeจะทำให้รายงานข้อผิดพลาดในการคอมไพล์กล่าวว่า:

การสร้างอินสแตนซ์โดยปริยายของเทมเพลตที่ไม่ได้กำหนด PrintType<void>

ดังนั้นเราจึงสามารถตรวจสอบได้ว่าthrowนิพจน์เป็นประเภทvoid(และใช่เครื่องหมายคำพูดมาตรฐานที่กล่าวถึงในคำตอบอื่น ๆ จะตรวจสอบได้ว่านี่ไม่ใช่ผลลัพธ์เฉพาะของการนำไปใช้งานแม้ว่า gcc จะมีปัญหาในการพิมพ์ข้อมูลที่มีค่าก็ตาม)

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