Kiedy chcemy używać static_assert
w sposób if constexpr
musimy 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 constexpr
nie 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>();
}
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_assert
zależy od T
, ale nie jestem pewien.
c++
templates
language-lawyer
c++17
static-assert
florestan
źródło
źródło
static_assert(False<int>, "AAA");
jest równoważne zstatic_assert(false, "AAA");
wewnątrz lambda.f(std::integral_constant<int, 1>{});
Wandbox, nie wyzwala potwierdzenia: wandbox.org/permlink/UFYAmYwtt1ptsndrOdpowiedzi:
From [stmt.if] / 2 ( wyróżnienie moje)
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)
Tak, naprawdę
False<T>
zależy od ciebieT
. Problem polega na tym, że ogólna lambda sama jest szablonem iFalse<T>
nie jest zależna od żadnego parametru szablonu lambda.Przez
T
toFalse<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
:Przykład na żywo
źródło
Zazwyczaj stosuje się tutaj [temp .res] / 8 :
Po instancję
foo<T>
Thestatic_assert
masz już nie zależy. Staje sięstatic_assert(false)
- dla wszystkich możliwych instancji operatora połączenia ogólnej lambdaf
. To źle sformułowane, nie wymaga diagnostyki. Clang diagnozuje, gcc nie. Obie są poprawne.Pamiętaj, że nie ma znaczenia, że
static_assert
tutaj zostanie odrzucony.To utrzymuje
static_assert
zależ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.źródło