Perbedaan antara C ++ 03 throw () specifier C ++ 11 noexcept


100

Apakah ada perbedaan antara throw()dan noexceptselain diperiksa pada waktu proses dan waktu kompilasi?

Artikel Wikipedia C ++ 11 ini menyarankan bahwa penentu lemparan C ++ 03 sudah tidak digunakan lagi.
Mengapa demikian, apakah noexceptcukup mampu untuk mencakup semua itu pada waktu kompilasi?

[Catatan: Saya telah memeriksa pertanyaan ini dan artikel ini , tetapi tidak dapat menentukan alasan kuat untuk tidak berlaku lagi.]


7
Menurut artikel bagus ini juga noexceptdapat menimbulkan pemeriksaan runtime. Perbedaan utama di antara mereka adalah bahwa memutus noexceptsebab std::terminatesementara melanggar throwsebab std::unexpected. Juga perilaku pelepasan tumpukan yang sedikit berbeda dalam kasus ini.
Fiktik

Tidak ada "waktu kompilasi" yang diperiksa dengan beberapa spesifikasi pengecualian yang "waktu proses" diperiksa pada spesifikasi lain. Itu hanya mitos yang dibuat oleh lawan dari spesifikasi pengecualian C ++.
wonderguy

Jawaban:


129

Penentu pengecualian tidak digunakan lagi karena penentu pengecualian umumnya merupakan ide yang buruk . noexceptditambahkan karena ini adalah satu-satunya penggunaan penentu pengecualian yang cukup berguna: mengetahui kapan suatu fungsi tidak akan mengeluarkan pengecualian. Jadi itu menjadi pilihan biner: fungsi yang akan melempar dan fungsi yang tidak akan melempar.

noexceptditambahkan daripada hanya menghapus semua penentu lemparan selain throw()karena noexceptlebih kuat. noexceptdapat memiliki parameter yang waktu kompilasi diselesaikan menjadi boolean. Jika boolean benar, maka noexcepttongkatnya. Jika boolean salah, maka noexcepttidak menempel dan fungsinya mungkin muncul.

Jadi, Anda bisa melakukan sesuatu seperti ini:

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

Apakah CreateOtherClassmembuang pengecualian? Mungkin, jika Tkonstruktor default bisa. Bagaimana kita membedakannya? Seperti ini:

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

Dengan demikian, CreateOtherClass()akan membuang iff dari tipe yang diberikan konstruktor default melempar. Ini memperbaiki salah satu masalah utama dengan penentu pengecualian: ketidakmampuannya untuk menyebarkan tumpukan panggilan.

Anda tidak dapat melakukan ini dengan throw().


+1 Jawaban yang berguna, bagi saya. Masih mencari jawaban yang mengatakan mengapa saya ingin menggunakan noexcept. Saya tidak pernah menggunakan throw()penentu, dan saya mencoba untuk menentukan apakah noexceptbenar-benar memberikan manfaat (selain dokumentasi yang diperiksa kompiler).
hmjd


1
@NicolBolas setuju. tetapi jika noexcept akan menjadi jaminan, kompilator dapat memeriksa apakah suatu fungsi dapat melempar atau tidak dalam destruktor. Jadi mampu memperingatkan programmer bahwa suatu fungsi tidak terkecuali atau tidak.
Alex

2
@NicolBolas panggilan runtime std::terminate. yang JAUH BURUK ! kode dapat menyelinap ke dalam rilis yang memiliki fungsi yang ditandai noexcept dan pada waktu proses (artinya di situs pelanggan) pelanggaran terdeteksi. Yang saya maksud adalah jaminan kompilator untuk menghasilkan kode yang tidak membuang pengecualian di tempat pertama.
Alex

2
@NicolBolas: Satu perbedaan lain yang perlu diperhatikan. Jika suatu fungsi ditandai throws()maka jika pengecualian dilemparkan, tumpukan harus dibatalkan hingga cakupan fungsi itu (sehingga semua variabel otomatis dalam fungsi dimusnahkan) di mana titik terminate()dipanggil (melalui unexpected()). Jika suatu fungsi ditandai noexceptmaka jika pengecualian dilemparkan maka terminate dipanggil (pelepasan tumpukan adalah implementasi yang ditentukan detail).
Martin York

33

noexcept tidak diperiksa pada waktu kompilasi.

Implementasi tidak boleh menolak ekspresi hanya karena ketika dijalankan itu melempar atau mungkin melempar pengecualian yang tidak diizinkan oleh fungsi yang memuatnya.

Ketika sebuah fungsi yang dideklarasikan noexceptatau throw()mencoba untuk menampilkan sebuah pengecualian, satu-satunya perbedaan adalah bahwa satu panggilan terminatedan panggilan lainnya unexpecteddan gaya penanganan pengecualian yang terakhir secara efektif sudah tidak digunakan lagi.


Tetapi jika fungsi virtual memiliki throw()/ noexcept, pemeriksaan waktu kompilasi memastikan bahwa overrider juga memilikinya.
penasaran

2

std::unexpected() dipanggil oleh runtime C ++ saat spesifikasi pengecualian dinamis dilanggar: pengecualian dilempar dari fungsi yang spesifikasi pengecualiannya melarang pengecualian jenis ini.

std::unexpected() juga dapat dipanggil langsung dari program.

Dalam kedua kasus, std::unexpectedpanggilan yang saat ini diinstal std::unexpected_handler. std::unexpected_handlerPanggilan default std::terminate.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.