Apakah pernah buruk untuk menandai fungsi constexpr C ++?

26

Diberi fungsi yang sangat sepele,

int transform(int val) {
    return (val + 7) / 8;
}

Seharusnya sangat jelas bahwa mudah untuk mengubah fungsi ini menjadi constexprfungsi, memungkinkan saya untuk menggunakannya ketika mendefinisikan constexprvariabel, seperti:

constexpr int transform(int val) {
    return (val + 7) / 8;
}

Asumsi saya adalah ini benar-benar perbaikan, karena fungsi masih dapat dipanggil dalam non- constexprkonteks, dan sekarang dapat juga digunakan untuk membantu mendefinisikan variabel konstanta waktu kompilasi.

Pertanyaan saya adalah, adakah situasi di mana ini adalah ide yang buruk? Seperti, dengan membuat fungsi ini constexpr, dapatkah saya menghadapi situasi di mana fungsi ini tidak lagi dapat digunakan dalam keadaan tertentu, atau di mana ia akan berperilaku buruk?

Xirema
sumber
1
Satu-satunya hal yang dapat saya pikirkan adalah bug kompiler. Ada kemungkinan bahwa panggilan fungsi constexpr rekursif dapat menyebabkan langkah kompilasi yang sangat lambat atau bahkan kompiler dari kehancuran memori.
Zan Lynx

Jawaban:

19

Ini hanya penting jika fungsi ini merupakan bagian dari antarmuka publik, dan Anda ingin menjaga versi API-biner yang kompatibel di masa depan. Dalam hal ini, Anda harus memikirkan dengan cermat bagaimana Anda ingin mengembangkan API Anda, dan di mana Anda memerlukan titik ekstensi untuk perubahan di masa mendatang.

Itu membuat constexprkualifikasi menjadi keputusan desain yang tidak dapat dibatalkan. Anda tidak dapat menghapus kualifikasi ini tanpa perubahan yang tidak kompatibel ke API Anda. Ini juga membatasi bagaimana Anda dapat mengimplementasikan fungsi itu, misalnya Anda tidak akan dapat melakukan logging dalam fungsi ini. Tidak setiap fungsi sepele akan tetap sepele dalam kekekalan.

Itu berarti Anda sebaiknya menggunakan constexpruntuk fungsi yang secara inheren fungsi murni, dan itu akan benar-benar berguna pada waktu kompilasi (misalnya untuk pemrograman template). Tidaklah baik untuk membuat fungsi constexpr hanya karena implementasi saat ini kebetulan menjadi constexpr-mampu.

Di mana evaluasi waktu kompilasi tidak diperlukan, menggunakan fungsi inline atau fungsi dengan tautan internal akan tampak lebih tepat constexpr. Semua varian ini memiliki kesamaan bahwa badan fungsi adalah "publik" dan tersedia di unit kompilasi yang sama dengan lokasi panggilan.

Jika fungsi yang dipermasalahkan bukan bagian dari API publik yang stabil, ini bukan masalah karena Anda dapat mengubah desain sesuka hati. Tetapi karena Anda sekarang mengontrol semua situs panggilan, tidak perlu menandai fungsi constexpr "berjaga-jaga". Anda tahu apakah Anda menggunakan fungsi ini dalam konteks constexpr. Menambahkan kualifikasi yang tidak perlu maka mungkin dianggap membingungkan.

amon
sumber
12

Menandai fungsi constexprjuga menjadikannya fungsi inline § [dcl.constexpr] / 1:

Fungsi atau anggota data statis yang dideklarasikan dengan specifier constexpr secara implisit merupakan fungsi atau variabel inline (7.1.6).

inline, pada gilirannya, berarti Anda perlu memasukkan definisi fungsi itu di setiap unit terjemahan yang dapat digunakan. Itu pada dasarnya berarti constexprfungsi harus berupa:

  1. terbatas untuk digunakan dalam satu unit terjemahan, atau
  2. didefinisikan dalam tajuk.

Sebagian besar fungsi khas yang ingin Anda deklarasikan di header dan tentukan dalam file sumber (dan apa pun yang menggunakannya hanya menyertakan header, lalu tautan ke file objek sumber itu) constexprtidak akan berfungsi.

Secara teori, saya kira Anda bisa memindahkan semuanya ke header dan hanya memiliki satu file sumber yang hanya mencakup semua header, tetapi ini akan merusak waktu kompilasi secara drastis, dan untuk sebagian besar proyek yang serius akan membutuhkan sejumlah besar memori untuk dikompilasi.

Suatu constexprfungsi juga dibatasi dalam beberapa hal, jadi untuk beberapa fungsi mungkin tidak menjadi pilihan sama sekali. Pembatasan meliputi:

  1. fungsi virtual tidak bisa constexpr.
  2. tipe kembalinya harus berupa 'tipe literal "(mis., tidak ada objek dengan ctor atau dtor non-trival).
  3. semua parameternya harus tipe literal.
  4. tubuh fungsi tidak dapat berisi tryblok.
  5. itu tidak dapat berisi definisi variabel dari tipe non-literal, atau apa pun dengan durasi penyimpanan statis atau thread.

Saya telah melewatkan beberapa hal yang agak tidak jelas (mis., Itu juga tidak dapat memuat a gotoasm pernyataan atau ), tetapi Anda mendapatkan ide - untuk beberapa hal, itu tidak akan berhasil.

Intinya: ya, ada beberapa situasi di mana ini akan menjadi ide yang buruk.

Jerry Coffin
sumber
"Itu tidak boleh virtual (sampai C ++ 20)" Saya bertanya-tanya bagaimana fungsi virtual bisa menjadi constexpr? Apa yang dilakukan kompiler?
chaosink