Me deparei com esse problema ao tentar me especializar tuple_size
/ tuple_element
para uma classe personalizada em C ++ 17 para ligação estruturada.
O código abaixo é compilado no GCC, mas não no clang (ambas as versões de tronco, veja o link abaixo).
#include <type_traits>
template<typename T, typename... Ts>
using sfinae_t = T;
template<typename T, bool... Bs>
using sfinae_v_t = sfinae_t<T, typename std::enable_if<Bs>::type...>;
template <typename T>
struct Test;
template <typename T>
struct Test<sfinae_v_t<T, std::is_integral_v<T>>> {};
void f() {
Test<int> t;
}
Este é o erro fornecido pelo clang:
<source>:13:8: error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list
struct Test<sfinae_v_t<T, std::is_integral<T>::value>> {};
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
Compiler returned: 1
Isso é um bug no compilador ou o código acima chama algum UB?
gcc
compile isso , visto que não compila isso ...Respostas:
O que eu digo abaixo (no OLD POST ) deve ser verdadeiro até certo ponto, mas o problema real é que SFINAE é usado incorretamente; portanto, não tenho mais tanta certeza de que isso seja um bug no gcc.
Uma declaração de alias deve sempre ter êxito, você não pode SFINAE lá, pois não é uma declaração ou especialização de classe ou função (isso faz sentido, pois você não pode se especializar em alias). Se a declaração do alias não for bem-sucedida, o programa está mal formado. Portanto, o compilador pode assumir que nunca ocorrerá caso a declaração de alias não seja bem-sucedida até que você o force a instanciar esse modelo.
Portanto, é perfeitamente aceitável para o compilador pensar que
sfinae_v_t<T,...>
é sempreT
, uma vez que isso acontecerá, quando o programa não estiver mal formado. Portanto, verá que, em todos os casos em que o programa não está mal formado, a especialização parcial não se especializa e, como tal, dirá que isso está mal formado. (Isso é o que clang faz).Eu não acho que o compilador seja forçado a fazer isso. E se não, e apenas pensa "Ok,
sfinae_v_t
é algum tipo, qualquer que seja.", Então não é óbvio que isso seja uma redeclaração. Então, acho que até instanciarmos um deles, não há nada errado em não lançar um erro.Mas quando a instanciamos, deve haver o problema que temos uma redeclaração ou que o programa está mal formado devido
std::enable_if
, dependendo do argumento do modelo. O GCC deve escolher pelo menos um deles, mas não o faz.Isso também não se aplica absolutamente ao exemplo mais fácil sem
std::enable_if
. Por isso, ainda acho que isso é um bug no GCC, mas estou suficientemente impressionado que não posso mais dizer isso com certeza. Eu diria apenas que alguém deveria relatar isso como um bug e deixar as pessoas do gcc pensarem sobre isso.POSTO ANTIGO
Este é um erro no gcc. O padrão nos fornece regras para converter um modelo de classe em modelos de função. Um modelo de classe é mais especializado que outro se sua função vier antes da outra na ordem parcial do modelo de função.
Eu criei as funções aqui e agora o gcc afirma que chamá-las é ambígua, portanto, também seria necessário dizer que os modelos de classe são igualmente especificados.
Nota: Lendo o padrão cuidadosamente, o compilador na minha cabeça concorda com o clang.
fonte
sfinae_v_t<T, std::is_integral_v<T>>
e sãosfinae_v_t<T, !std::is_integral_v<T>>
tratados como os mesmos tipos? Semântica, eles não são.sfinae_v_t<T>
depende deleT
? Nesse caso, eles não seriam os mesmos porque qualquer um será mal formado.enable_if_t
. Minha melhor leitura do padrão é que não importa se são iguais ou não. Para a ordem parcial, sempre compararemos a forma de parâmetro do modelo de uma função com a forma de argumento do modelo da outra (ou seja, aint
já está substituída) e, em seguida, haverá um tipo real em uma delas, portanto, não precisamos comparar eles abstratamente.template<bool B, typename T> enable_if_t = typename enable_if<B, T>::type;
não funcionaria. Vou seguir em frente e registrar o bug no gcc, mas não tenho certeza se o gcc está errado lá. Obrigado.