jeśli constexpr z static_assert w lambda, który kompilator jest poprawny?

13

Kiedy chcemy używać static_assertw sposób if constexprmusimy warunek zależny od jakiegoś parametru szablonu. Co ciekawe, gcc i clang nie zgadzają się, gdy kod jest zawarty w lambda.

Poniższy kod kompiluje się z gcc, ale clang wyzwala aser, nawet jeśli if constexprnie może to być prawda.

#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>();
}

Przykład na żywo tutaj .

Można to łatwo naprawić, zastępując False<T>przez False<decltype(x)>.

Pytanie brzmi: który kompilator ma rację? Zakładam, że gcc jest poprawny, ponieważ warunek w static_assertzależy od T, ale nie jestem pewien.

florestan
źródło
Ten pyta niby to samo pytanie, ale pochodzących z kierunku przeciwnego: stackoverflow.com/questions/59393908/... .
NathanOliver
1
@mfnx Nie można odtworzyć . Czy możesz podać przykład?
NathanOliver
2
Powiedziałbym, że oba mają rację (źle sformowany NDR): static_assert(False<int>, "AAA");jest równoważne z static_assert(false, "AAA");wewnątrz lambda.
Jarod42
2
@mfnx Zmieniłeś wartość stałej. Użycie przykładu OP, w którym stałą jest f(std::integral_constant<int, 1>{});Wandbox, nie wyzwala potwierdzenia: wandbox.org/permlink/UFYAmYwtt1ptsndr
NathanOliver
1
@NathanOliver Tak, masz rację, przepraszam za hałas. Wydaje się, że gcc ma rację, ponieważ ten kod w constexpr powinien zostać odrzucony, jeśli stała> = 0;
mfnx

Odpowiedzi:

1

From [stmt.if] / 2 ( wyróżnienie moje)

Jeżeli instrukcja if ma postać if constexpr, wartość warunku będzie konwertowanym kontekstowo stałym wyrażeniem typu bool; ta forma nazywa się instrukcją if constexpr. Jeśli wartość przekonwertowanego warunku jest fałszywa, pierwsza podstacja jest odrzuconą instrukcją, w przeciwnym razie druga podstacja, jeśli jest obecna, jest odrzuconą instrukcją. Podczas tworzenia instancji obejmującej encję szablonową ([temp .pre]), jeśli warunek nie jest zależny od wartości po jego instancji, odrzucone podstąpienie (jeśli występuje) nie jest tworzone.

Czytając to, można by pomyśleć, że twierdzenie statyczne zostanie odrzucone, ale tak nie jest.

Potwierdzenie statyczne jest uruchamiane w pierwszej fazie szablonu, ponieważ kompilator wie, że zawsze jest fałszywy.

From [temp.res] / 8 ( wyróżnienie moje)

Ważność szablonu można sprawdzić przed każdą instancją. [ Uwaga: Znajomość nazw typów pozwala na sprawdzenie składni każdego szablonu w ten sposób. - uwaga końcowa ] Program jest źle sformułowany, diagnostyka nie jest wymagana, jeśli:

  • (8.1) nie można wygenerować żadnej specjalizacji dla szablonu lub podstawienia constexpr, jeśli instrukcja w szablonie i szablon nie jest utworzony , lub

[...]

Tak, naprawdę False<T>zależy od ciebie T. Problem polega na tym, że ogólna lambda sama jest szablonem i False<T>nie jest zależna od żadnego parametru szablonu lambda.

Przez Tto False<T>jest fałszywe, assert statyczne zawsze będzie fałszywa, bez względu na to, który szablon argumentem jest wysyłany do lambda.

Kompilator może zobaczyć, że dla dowolnej instancji szablonu operator(), statyczne potwierdzenie zawsze będzie wyzwalane dla bieżącego T. Stąd błąd kompilatora.

Rozwiązaniem tego byłoby uzależnienie od 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>{});
}

Przykład na żywo

Racilla Guillaume
źródło
13

Zazwyczaj stosuje się tutaj [temp .res] / 8 :

Program jest źle sformułowany, nie jest wymagana diagnostyka, jeśli: nie można wygenerować prawidłowej specjalizacji dla szablonu lub podstawienia constexpr, jeśli instrukcja w szablonie i szablon nie jest utworzony

Po instancję foo<T>The static_assertmasz już nie zależy. Staje się static_assert(false)- dla wszystkich możliwych instancji operatora połączenia ogólnej lambda f. To źle sformułowane, nie wymaga diagnostyki. Clang diagnozuje, gcc nie. Obie są poprawne.

Pamiętaj, że nie ma znaczenia, że static_asserttutaj zostanie odrzucony.

Można to łatwo naprawić, zastępując False<T>przez False<decltype(x)>.

To utrzymuje static_assertzależność w obrębie ogólnej lambda, a teraz dochodzimy do stanu, w którym hipotetycznie mogłaby istnieć ważna specjalizacja, więc nie jesteśmy już źle uformowani, ndr.

Barry
źródło