Di C ++, jika throw adalah ekspresi, apa tipenya?

115

Saya mengambil ini di salah satu upaya singkat saya ke reddit:

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

Pada dasarnya, penulis menunjukkan bahwa di C ++:

throw "error"

adalah ekspresi. Ini sebenarnya dijelaskan dengan cukup jelas dalam Standar C ++, baik dalam teks utama maupun tata bahasa. Namun, yang tidak jelas (setidaknya bagi saya) adalah jenis ekspresi apa? Saya menebak " void", tetapi sedikit bereksperimen dengan g ++ 4.4.0 dan Comeau menghasilkan kode ini:

    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
    }

Kompiler tidak memiliki masalah dengan // 1 tetapi muncul di // 2 karena tipe dalam operator bersyarat berbeda. Jadi jenis throwekspresi sepertinya tidak kosong.

Jadi apa itu?

Jika Anda menjawab, harap dukung pernyataan Anda dengan kutipan dari Standard.


Ini ternyata bukan tentang jenis ekspresi lemparan melainkan bagaimana operator bersyarat menangani ekspresi lemparan - sesuatu yang pasti tidak saya ketahui sebelum hari ini. Terima kasih untuk semua yang menjawab, tetapi khususnya untuk David Thornley.


sumber
10
+1 Pertanyaan yang mengagumkan. Dan cara cerdas untuk mengujinya.
Jeremy Powell
1
Tautan itu tampaknya membuatnya cukup jelas bahwa jenisnya ditentukan oleh kompiler menjadi apa pun yang dibutuhkan.
Draemon
Artikel terkait telah saya pikir telah diperbarui sejak saya melihatnya, dan saya yakin itulah masalahnya. Namun, saya tidak dapat menemukannya dalam standar.
A dan mungkin tidak - dobel d = lempar "foo"; adalah kesalahan dengan g + = (belum mengujinya dengan comeau)
+1 Saya penasaran ingin tahu jawabannya.
AraK

Jawaban:

96

Menurut standar, 5.16 paragraf 2 poin pertama, "Operan kedua atau ketiga (tetapi tidak keduanya) adalah ekspresi-lemparan (15.1); hasilnya adalah jenis yang lain dan merupakan nilai r." Oleh karena itu, operator kondisional tidak peduli apa tipe ekspresi-lemparan itu, tetapi hanya akan menggunakan tipe lainnya.

Faktanya, 15.1, paragraf 1 mengatakan secara eksplisit "Ekspresi lemparan adalah tipe kosong."

David Thornley
sumber
9
Oke - saya pikir kami memiliki pemenang.
Perhatikan bahwa ekspresi-lemparan adalah ekspresi-tugas. Jadi mereka adalah kesalahan sintaks sebagai argumen bagi sebagian besar operator. Jelas, Anda dapat menyembunyikannya dalam tanda kurung, tetapi jika mereka tidak diabaikan (argumen pertama operator bawaan, misalnya), itu adalah kesalahan tipe.
Pemrogram
4
Yang benar-benar mengejutkan saya adalah mereka memikirkan kasus ini dan membuat sesuatu yang masuk akal terjadi.
Omnifarious
31

"Ekspresi lemparan adalah tipe kosong"

ISO14882 Bagian 15

Draemon
sumber
Lalu g ++ dan Comeau lalai karena tidak memberikan kesalahan untuk // 1 kasus saya?
2
@Neil, tidak juga karena menurut C ++ / 5.16 / 2, operan kedua dan ketiga operator bersyarat bisa tipevoid
mloskot
13

Dari [expr.cond.2] (operator bersyarat ?:):

Jika operan kedua atau ketiga memiliki tipe (mungkin memenuhi syarat cv) kosong, maka konversi standar lvalue-to-rvalue, array-to-pointer, dan function-to-pointer dilakukan pada operan kedua dan ketiga, dan salah satu dari berikut ini akan berlaku:

- Operan kedua atau ketiga (tapi tidak keduanya) adalah ekspresi-lemparan; hasilnya adalah jenis yang lain dan merupakan nilai r.

- Baik operan kedua dan ketiga memiliki tipe void; hasilnya adalah tipe void dan merupakan rvalue. [Catatan: ini termasuk kasus di mana kedua operan adalah ekspresi-lemparan. - catatan akhir]

Jadi, dengan //1Anda berada dalam kasus pertama, dengan //2, Anda melanggar "salah satu dari yang berikut akan berlaku", karena tidak ada yang melakukannya, dalam kasus itu.

Marc Mutz - mmutz
sumber
3

Anda dapat memiliki printer tipe yang mengeluarkannya untuk Anda :

template<typename T>
struct PrintType;

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

Pada dasarnya kurangnya implementasi untuk PrintTypeakan menyebabkan laporan kesalahan kompilasi mengatakan:

Instansiasi implisit dari template yang tidak ditentukan PrintType<void>

jadi kami benar-benar dapat memverifikasi bahwa throwekspresi memiliki tipe void(dan ya, kutipan Standar yang disebutkan dalam jawaban lain memverifikasi bahwa ini bukan hasil spesifik implementasi - meskipun gcc mengalami kesulitan mencetak info berharga)

Nikos Athanasiou
sumber