Sah untuk menginisialisasi array dalam konstruktor constexpr?

11

Apakah kode berikut ini sah?

template <int N>
class foo {
public:
    constexpr foo()
    {
        for (int i = 0; i < N; ++i) {
            v_[i] = i;
        }
    }

private:
    int v_[N];
};

constexpr foo<5> bar;

Dentang menerimanya, tetapi GCC dan MSVC menolaknya.

Kesalahan GCC adalah:

main.cpp:15:18: error: 'constexpr foo<N>::foo() [with int N = 5]' called in a constant expression
   15 | constexpr foo<5> bar;
      |                  ^~~
main.cpp:4:15: note: 'constexpr foo<N>::foo() [with int N = 5]' is not usable as a 'constexpr' function because:
    4 |     constexpr foo()
      |               ^~~
main.cpp:4:15: error: member 'foo<5>::v_' must be initialized by mem-initializer in 'constexpr' constructor
main.cpp:12:9: note: declared here
   12 |     int v_[N];
      |         ^~

Jika kode semacam ini OK, saya bisa memotong beberapa penggunaan index_sequences.

Yongwei Wu
sumber
1
Gcc10 menerimanya juga.
songyuanyao
dapatkah Anda menyalin kesalahan dari MSVC?
Maks66
... dan GCC juga.
Evg
1
@songyuanyao - g ++ 10 menerimanya mengkompilasi C ++ 20; menolak kompilasi C ++ 17 atau lebih; intinya nampaknya yang _vharus diinisialisasi dalam daftar inisialisasi, hingga C ++ 17. Mungkin diubah sesuatu di C ++ 20.
Maks66
2
@ Evg Itu sebenarnya menarik, karena mungkin menyarankan Clang menggunakan "kesadarannya" bahwa objek durasi penyimpanan statis menjadi nol untuk mengatakan "oke, objek ini mungkin telah diinisialisasi default tetapi membaca dari intanggotanya tidak akan pernah memiliki perilaku yang tidak terdefinisi ". Saya ingin tahu apakah GCC tidak melakukan yang sesuai, atau sebaliknya ...
Lightness Races di Orbit

Jawaban:

14

Inisialisasi default sepele dilarang dalam constexprkonteks sampai C ++ 20 .

Alasannya, saya menduga, adalah mudah untuk "secara tidak sengaja" membaca dari primitif yang diinisialisasi default, tindakan yang memberikan program Anda perilaku tidak terdefinisi, dan ekspresi dengan perilaku tidak terdefinisi secara langsung dilarang menjadi constexpr( ref ). Bahasa telah diperluas sehingga sehingga sekarang kompiler harus memeriksa apakah pembacaan tersebut terjadi dan, jika tidak, inisialisasi default harus diterima. Ini sedikit lebih banyak pekerjaan untuk kompiler, tetapi (seperti yang Anda lihat!) Memiliki manfaat besar bagi programmer.

Makalah ini mengusulkan izin inisialisasi default untuk tipe konstruktif sepele default dalam konteks constexpr sambil terus menolak permintaan perilaku yang tidak terdefinisi. Singkatnya, selama nilai-nilai yang tidak diinisialisasi tidak dibaca, status tersebut harus diizinkan dalam constexpr di kedua skenario alokasi tumpukan dan tumpukan.

Sejak C ++ 20, sah untuk membiarkan v_"tidak diinisialisasi" seperti yang Anda miliki. Kemudian Anda pergi untuk menetapkan semua nilai elemennya, yang bagus.

Lightness Races di Orbit
sumber
4
@ max66 Aku juga! Yang saya lakukan adalah memindai daftar perubahan C ++ 20 di Wikipedia, menemukan sesuatu yang relevan constexpr, dan membaca sekilas proposal yang ditautkan;)
Lightness Races di Orbit
3
Bagian yang buruk adalah bahwa lebih dari 20 tahun saya menggunakan C ++. Jika setiap hari saya belajar sesuatu yang baru ... atau saya seorang programmer yang buruk atau C ++ menjadi terlalu rumit.
Maks66
5
@ max66 Hampir pasti yang terakhir. Juga fakta bahwa ia terus berubah secara fundamental setiap beberapa tahun menjadikannya target yang bergerak cepat. Siapa yang bisa mengimbanginya? Bahkan kompiler tidak mengikuti itu.
Lightness Races di Orbit
@ max66 Makalah ini terlintas dalam pikiran: Ingatlah Vasa!
Evg
@ Evg Oh, wow, kertas itu telah melewati saya (BESI). Temukan!
Lightness Races di Orbit