Cuando queremos usar a static_assert
en a if constexpr
debemos hacer que la condición dependa de algún parámetro de plantilla. Curiosamente, gcc y clang no están de acuerdo cuando el código está envuelto en una lambda.
El siguiente código se compila con gcc, pero clang activa la afirmación, incluso si if constexpr
no puede ser cierto.
#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>();
}
Se puede arreglar fácilmente sustituyendo False<T>
por False<decltype(x)>
.
Entonces la pregunta es: ¿qué compilador es el correcto? Supongo que gcc es correcto porque la condición en el static_assert
depende T
, pero no estoy seguro.
c++
templates
language-lawyer
c++17
static-assert
Florestan
fuente
fuente
static_assert(False<int>, "AAA");
es equivalente alstatic_assert(false, "AAA");
interior de la lambda.f(std::integral_constant<int, 1>{});
Wandbox no activa la afirmaciónRespuestas:
De [stmt.if] / 2 (énfasis mío)
Leyendo que uno pensaría que la afirmación estática se descartaría, pero este no es el caso.
La afirmación estática se activa en la primera fase de la plantilla porque el compilador sabe que siempre es falsa.
De [temp.res] / 8 (énfasis mío)
Sí, de hecho,
False<T>
depende de ustedT
. El problema es que un lambda genérico es en sí mismo una plantilla yFalse<T>
no depende de ningún parámetro de plantilla del lambda.Para un
T
queFalse<T>
es falso, la aserción estática siempre será falsa, sin importar qué argumento de plantilla se envíe a la lambda.El compilador puede ver que para cualquier instanciación de la plantilla
operator()
, la aserción estática siempre se activará para la T. actual. De ahí el error del compilador.Una solución para esto sería depender de
x
:Ejemplo en vivo
fuente
La regla habitual aquí es [temp.res] / 8 :
Una vez que crea una instancia
foo<T>
, lostatic_assert
que tiene ya no depende. Se conviertestatic_assert(false)
, para todas las posibles instancias del operador de llamada de la lambda genéricaf
. Eso está mal formado, no se requiere diagnóstico. Diagnósticos de argot, gcc no. Ambos son correctos.Tenga en cuenta que no importa que el
static_assert
aquí se descarte.Esto mantiene al
static_assert
dependiente dentro del lambda genérico, y ahora entramos en un estado donde hipotéticamente podría haber una especialización válida, por lo que ya no estamos mal formados, ndr.fuente