inconsistência clang / gcc na especialização de classe

9

Me deparei com esse problema ao tentar me especializar tuple_size/ tuple_elementpara 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;
}

https://godbolt.org/z/ztuRSq

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?

ofo
fonte
3
Isso pode ser simplificado ainda mais .
Evg 15/01
3
O ICC e o MSVC também não conseguem compilar.
ChrisMM 15/01
@ Evg É surpreendente que gcccompile isso , visto que não compila isso ...
Max Langhof 15/01
11
FWIW esta deve ser mal-formado se eu não estou completamente confundido (pela mesma razão que este é mal-formado).
Max Langhof 15/01
11
já que estamos citando padrão, adicionei a tag language-advogado.
Guillaume Racicot 15/01

Respostas:

3

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,...>é sempre T, 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.

n314159
fonte
São sfinae_v_t<T, std::is_integral_v<T>>e são sfinae_v_t<T, !std::is_integral_v<T>>tratados como os mesmos tipos? Semântica, eles não são.
ofo 15/01
@GuillaumeRacicot Muito possivelmente, mas eu gostaria de entender por que exatamente. Por exemplo, o padrão também diz "Os nomes dependentes não podem ser verificados ao declarar a especialização parcial, mas serão verificados ao substituir a especialização parcial". Isso não significa se o mesmo tipo deve ser decidido após a substituição de T na especialização parcial, uma vez que sfinae_v_t<T>depende dele T? Nesse caso, eles não seriam os mesmos porque qualquer um será mal formado.
ofo 15/01
@ofo Eu tenho que dizer, não tenho certeza. É um pouco assustador pensar sobre esses dois, já que um deles nunca será um tipo e usá-los em um contexto não-modelo resultará em um erro de compilação devido ao 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, a intjá está substituída) e, em seguida, haverá um tipo real em uma delas, portanto, não precisamos comparar eles abstratamente.
n314159 15/01
11
Indo mais fundo, eu encontrei este de aqui . O SFINAE deve funcionar bem com aliases de modelo, caso contrário, também 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.
ofo 15/01