jika constexpr dengan static_assert di lambda, kompiler mana yang benar?

13

Ketika kita ingin menggunakan static_assertdalam if constexprkita harus membuat kondisi tergantung pada beberapa parameter template. Menariknya, gcc dan dentang tidak setuju ketika kode tersebut dibungkus dengan lambda.

Kode berikut dikompilasi dengan gcc, tetapi dentang memicu pernyataan, bahkan jika itu if constexprtidak benar.

#include <utility>

template<typename T> constexpr std::false_type False;

template<typename T>
void foo() {

    auto f = [](auto x) {
        constexpr int val = decltype(x)::value;
        if constexpr(val < 0) {
            static_assert(False<T>, "AAA");
        }
    };

    f(std::integral_constant<int, 1>{});
}

int main() {
    foo<int>();
}

Contoh langsung di sini .

Itu dapat dengan mudah diperbaiki dengan mengganti False<T>dengan False<decltype(x)>.

Jadi pertanyaannya adalah: kompiler mana yang benar? Saya berasumsi bahwa gcc benar karena kondisi di static_asserttergantung pada T, tetapi saya tidak yakin.

penghijauan
sumber
Ini menanyakan jenis pertanyaan yang sama tetapi datang dari arah yang berlawanan: stackoverflow.com/questions/59393908/… .
NathanOliver
1
@ mfnx Tidak dapat mereproduksi . Bisakah Anda membagikan contoh?
NathanOliver
2
Saya akan mengatakan keduanya benar (NDR terbentuk buruk): static_assert(False<int>, "AAA");setara dengan static_assert(false, "AAA");di dalam lambda.
Jarod42
2
@ mfnx Anda telah mengubah nilai konstanta. Menggunakan contoh OP di mana konstanta adalah f(std::integral_constant<int, 1>{});Wandbox tidak memicu pernyataan: wandbox.org/permlink/UFYAmYwtt1ptsndr
NathanOliver
1
@NathanOliver Ya Anda benar, maaf untuk kebisingannya. Tampaknya gcc tepat karena kode di constexpr harus dibuang jika konstanta> = 0;
mfnx

Jawaban:

1

Dari [stmt.if] / 2 (penekanan pada saya)

Jika pernyataan if dalam bentuk if constexpr, nilai kondisi harus berupa ekspresi konstanta konstan tipe bool; formulir ini disebut pernyataan constexpr if. Jika nilai kondisi yang dikonversi salah, substatemen pertama adalah pernyataan yang dibuang, sebaliknya subtatement kedua, jika ada, adalah pernyataan yang dibuang. Selama instantiasi entitas templated terlampir ([temp.pre]), jika kondisi tidak bergantung pada nilai setelah instantiasinya, subtasi dibuang (jika ada) tidak dipakai.

Membaca bahwa orang akan berpikir pernyataan statis akan dihapus, tetapi ini tidak terjadi.

Pernyataan statis dipicu pada fase pertama templat karena kompilator tahu itu selalu salah.

Dari [temp.res] / 8 (penekanan pada saya)

Validitas templat dapat diperiksa sebelum instantiasi. [ Catatan: Mengetahui nama mana yang merupakan nama jenis memungkinkan sintaks setiap templat diperiksa dengan cara ini. - catatan akhir ] Program ini salah bentuk, tidak diperlukan diagnostik, jika:

  • (8.1) tidak ada spesialisasi yang valid dapat dihasilkan untuk templat atau substatement dari constexpr jika pernyataan di dalam templat dan templat tidak dipakai , atau

[...]

Ya, False<T>tergantung pada Anda T. Masalahnya adalah bahwa lambda generik itu sendiri merupakan templat, dan False<T>tidak bergantung pada parameter templat dari lambda.

Untuk Tyang False<T>salah, pernyataan statis akan selalu salah, tidak peduli argumen templat mana yang dikirim ke lambda.

Compiler dapat melihat bahwa untuk setiap instantiation dari template operator(), pernyataan statis akan selalu memicu untuk T. saat ini.

Solusi untuk ini adalah bergantung pada x:

template<typename T>
void foo() {

    auto f = [](auto x) {
        if constexpr(x < 0) {
            static_assert(False<decltype(x)>, "AAA");
        }
    };

    f(std::integral_constant<int, 1>{});
}

Contoh langsung

Guillaume Racicot
sumber
13

Aturan biasa di sini adalah [temp.res] / 8 :

Program ini salah bentuk, tidak diperlukan diagnostik, jika: tidak ada spesialisasi yang valid dapat dihasilkan untuk templat atau substatement dari constexpr jika pernyataan dalam templat dan templat tidak dipakai

Setelah Anda instantiate foo<T>, yang static_assertAnda miliki tidak lagi tergantung. Ini menjadi static_assert(false)- untuk semua instantiasi yang mungkin dari operator panggilan lambda generik f. Itu buruk bentuknya, tidak diperlukan diagnostik. Dentang diagnosis, gcc tidak. Keduanya benar.

Perhatikan bahwa tidak peduli bahwa static_assertdi sini adalah dibuang.

Itu dapat dengan mudah diperbaiki dengan mengganti False<T>dengan False<decltype(x)>.

Ini menjaga static_assertketergantungan dalam lambda generik, dan sekarang kita masuk ke keadaan di mana ada hipotesis yang bisa menjadi spesialisasi yang valid, jadi kita tidak lagi sakit, ndr.

Barry
sumber