O GCC aceita esse código, o Clang e o MSVC o rejeitam devido à falha de asserção estática em assert
. O que o padrão diz?
https://godbolt.org/z/PKMKzYGsc
template<typename T>
constexpr int assert() {
static_assert(sizeof(T) == 1);
return 1;
}
template<auto I>
using Int = int;
template<typename T> requires (sizeof(T) == 1)
constexpr auto foo(T) -> Int<assert<T>()> {
return 1;
}
template<typename T> requires (sizeof(T) > 1)
constexpr auto foo(T a) -> Int<1> {
return 2;
}
static_assert(foo('1') == 1);
static_assert(foo(2) == 2);
Saída do Clang:
<source>:3:19: error: static assertion failed due to requirement 'sizeof(int) == 1'
3 | static_assert(sizeof(T) == 1);
| ^~~~~~~~~~~~~~
<source>:11:30: note: in instantiation of function template specialization 'assert<int>' requested here
11 | constexpr auto foo(T) -> Int<assert<T>()> {
| ^
<source>:21:15: note: while substituting deduced template arguments into function template 'foo' [with T = int]
21 | static_assert(foo(2) == 2);
| ^
<source>:3:29: note: expression evaluates to '4 == 1'
3 | static_assert(sizeof(T) == 1);
Durante a dedução do argumento do template, a verificação de satisfação das restrições associadas do template de função ocorre antes da substituição dos argumentos do template no tipo de função. Isso é descrito em [temp.deduct.general]/5 :
Isso significa que a dedução de tipo deve falhar antes de encontrar o problema
assert
neste exemplo, tornando-o bem formado.Este resultado é o resultado do CWG2369 , antes do qual os dois passos acima aconteceram na ordem oposta. Evidentemente, Clang e MSVC não implementam essa mudança ainda, levando à divergência de implementação observada.
O programa está bem formado e o gcc está correto ao aceitar o código conforme explicado abaixo.
De over.over :
(ênfase minha)
Isso significa que para a chamada
foo(2)
, a versão#1
comsizeof(T)==1
será eliminada do conjunto de funções selecionadas. E a outra (versão#2
) que satisfaz a restrição será a única candidata viável e usada.