Quando queremos usar a static_assert
em a if constexpr
, devemos tornar a condição dependente de algum parâmetro do modelo. Curiosamente, gcc e clang discordam quando o código é agrupado em uma lambda.
O código a seguir é compilado com o gcc, mas o clang aciona a declaração, mesmo que if constexpr
não possa ser verdadeira.
#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>();
}
Pode ser facilmente corrigido substituindo False<T>
por False<decltype(x)>
.
Então a pergunta é: qual compilador está certo? Eu diria que o gcc está correto porque a condição no static_assert
é dependente T
, mas não tenho certeza.
c++
templates
language-lawyer
c++17
static-assert
Florestan
fonte
fonte
static_assert(False<int>, "AAA");
é equivalente astatic_assert(false, "AAA");
dentro do lambda.f(std::integral_constant<int, 1>{});
Wandbox, não aciona a afirmação: wandbox.org/permlink/UFYAmYwtt1ptsndrRespostas:
De [stmt.if] / 2 (ênfase minha)
Lendo que alguém pensaria que a afirmação estática seria descartada, mas esse não é o caso.
A declaração estática é acionada na primeira fase do modelo, porque o compilador sabe que é sempre falso.
De [temp.res] / 8 (ênfase minha)
Sim, de fato, você
False<T>
dependeT
. O problema é que um lambda genérico é ele próprio um modelo eFalse<T>
não depende de nenhum parâmetro de modelo do lambda.Para um
T
queFalse<T>
é falso, a afirmação estática sempre será falsa, independentemente do argumento do modelo enviado para o lambda.O compilador pode ver que, para qualquer instanciação do modelo
operator()
, a declaração estática sempre dispara para o T. atual. Portanto, o erro do compilador.Uma solução para isso seria depender de
x
:Exemplo ao vivo
fonte
A regra usual aqui é [temp.res] / 8 :
Depois de instanciar
foo<T>
, o questatic_assert
você tem não é mais dependente. Torna-sestatic_assert(false)
- para todas as instâncias possíveis do operador de chamada do lambda genéricof
. Isso é mal formado, não é necessário diagnóstico. Diagnósticos clang, gcc não. Ambos estão corretos.Observe que não importa que o
static_assert
aqui seja descartado.Isso mantém o
static_assert
dependente dentro do lambda genérico, e agora chegamos a um estado em que hipoteticamente poderia haver uma especialização válida; portanto, não estamos mais mal formados, ndr.fonte