inconsistencia de clang / gcc en la especialización de clase

9

Encontré este problema al intentar especializar tuple_size/ tuple_elementpara una clase personalizada en C ++ 17 para el enlace estructurado.

El siguiente código se compila en GCC, pero no en clang (ambas versiones troncales, ver el enlace a continuación).

#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 es el error proporcionado por 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

¿Es esto un error en el compilador o el código anterior invoca alguna UB?

ofo
fuente
3
Esto se puede simplificar aún más .
Evg
3
ICC y MSVC tampoco pueden compilarse.
ChrisMM
@Evg Es sorprendente que gcccompile eso, ya que no compila esto ...
Max Langhof
1
FWIW esto debería estar mal formado si no estoy completamente equivocado (por la misma razón que esto está mal formado).
Max Langhof
1
Como estamos citando el estándar, agregué la etiqueta de idioma-abogado.
Guillaume Racicot

Respuestas:

3

Lo que digo a continuación (bajo POSTE ANTIGUO ) debería ser cierto hasta cierto punto, pero el problema real con esto es que SFINAE se usa incorrectamente, por lo tanto, ya no estoy tan seguro de que esto sea un error en gcc.

Una declaración de alias siempre debe tener éxito, no puede SFINAE allí, ya que no es una declaración de clase o función o especializaciones (eso tiene sentido, ya que no puede especializar alias). Si la declaración de alias no tiene éxito, el programa está mal formado. Por lo tanto, el compilador puede suponer que nunca llegará el caso de que la declaración de alias no tenga éxito hasta que lo obligue a crear una instancia de dicha plantilla.

Por lo tanto, es perfectamente aceptable que el compilador piense que sfinae_v_t<T,...>siempre es así T, ya que eso sucederá, cuando el programa no está mal formado. Por lo tanto, verá que en todos los casos en que el programa no está mal formado, la especialización parcial no se especializa y, como tal, le dirá que está mal formado. (Eso es lo que hace el ruido metálico).

No creo que el compilador se vea obligado a hacer esto. Y si no lo hace, y solo piensa "Ok, sfinae_v_tes algún tipo, lo que sea", entonces no es obvio que esto sea una redeclaración. Así que creo que hasta que instanciamos uno de ellos no hay nada de malo en no arrojar un error.

Pero cuando lo instanciamos, debería existir el problema de que tenemos una redeclaración o que el programa está mal formado debido std::enable_if, dependiendo del argumento de la plantilla. GCC debería recoger al menos uno de ellos, pero ninguno lo hace.

Esto tampoco se aplica absolutamente al ejemplo más fácil sin std::enable_if. Así que todavía creo que esto es un error en GCC, pero estoy lo suficientemente loco como para no poder decir eso con certeza. Solo diría que alguien debería informarlo como un error y dejar que la gente de gcc lo piense.

ANTIGUA POST

Este es un error en gcc. El estándar nos da reglas para convertir una plantilla de clase en plantillas de función. Una plantilla de clase es más especializada que otra si su función es anterior a la otra en el orden de la plantilla de función parcial.

Creé las funciones aquí y ahora gcc afirma que llamarlas es ambiguo, por lo tanto, también tendría que decir que las plantillas de clase están igualmente especificadas.

Nota: Leyendo el estándar cuidadosamente, el compilador en mi cabeza está de acuerdo con el sonido metálico.

n314159
fuente
¿Son sfinae_v_t<T, std::is_integral_v<T>>y son sfinae_v_t<T, !std::is_integral_v<T>>tratados como los mismos tipos? Semánticamente, no lo son.
ofo
@GuillaumeRacicot Muy posiblemente, pero me gustaría entender por qué exactamente. Por ejemplo, el estándar también dice "Los nombres dependientes no se pueden verificar al declarar la especialización parcial, pero se verificarán al sustituir en la especialización parcial". ¿No significa eso si se debe decidir si son del mismo tipo después de sustituir T en una especialización parcial, ya sfinae_v_t<T>que depende de T? En cuyo caso, no serían lo mismo porque ninguno de los dos estará mal formado.
OFO
@ofo Tengo que decir que no estoy seguro. Es un poco loco incluso pensar en esos dos, ya que uno de ellos nunca será un tipo y usarlos en un contexto que no sea de plantilla dará como resultado un error de compilación debido a enable_if_t. Mi mejor lectura del estándar es que no importa si son iguales o no. Para el ordenamiento parcial siempre compararemos la forma del parámetro templare de una función con la forma del argumento de plantilla de la otra (es decir, intya está sustituido) y luego hay un tipo real en una de ellas, por lo que no tenemos que comparar ellos de manera abstracta.
n314159
1
Profundizando, encontré esto desde aquí . SFINAE debería funcionar bien con alias de plantilla, de lo contrario template<bool B, typename T> enable_if_t = typename enable_if<B, T>::type;tampoco funcionaría. Seguiré adelante y archivaré el error contra gcc, pero no estoy seguro de si gcc está mal allí. Gracias.
OFO