incohérence clang / gcc dans la spécialisation de classe

9

J'ai rencontré ce problème en essayant de spécialiser tuple_size/ tuple_elementpour une classe personnalisée en C ++ 17 pour la liaison structurée.

Le code ci-dessous se compile dans GCC, mais pas dans clang (les deux versions de tronc, voir le lien ci-dessous).

#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

C'est l'erreur fournie par 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

Est-ce un bogue dans le compilateur ou le code ci-dessus appelle-t-il de l'UB?

ofo
la source
3
Cela peut être encore plus simplifié .
Evg
3
ICC et MSVC ne parviennent pas non plus à compiler également.
ChrisMM
@Evg C'est surprenant que ça gcccompile, vu qu'il ne compile pas ça ...
Max Langhof
1
FWIW cela devrait être mal formé si je ne suis pas tout à fait erronée (pour la même raison que c'est mal formé).
Max Langhof
1
depuis que nous citons la norme, j'ai ajouté la balise langue-avocat.
Guillaume Racicot le

Réponses:

3

Ce que je dis ci-dessous (sous OLD POST ) devrait être vrai dans une certaine mesure, mais le problème réel avec cela est que SFINAE est mal utilisé, donc je ne suis plus sûr que ce soit un bug dans gcc.

Une déclaration d'alias doit toujours réussir, vous ne pouvez pas y mettre SFINAE, car ce n'est pas une déclaration de classe ou de fonction ou des spécialisations (cela a du sens, car vous ne pouvez pas spécialiser les alias). Si la déclaration d'alias échoue, le programme est mal formé. Par conséquent, le compilateur peut supposer qu'il ne se produira jamais que la déclaration d'alias échoue tant que vous ne l'avez pas forcée à instancier un tel modèle.

Par conséquent, il est parfaitement acceptable pour le compilateur de penser que sfinae_v_t<T,...>c'est toujours T, puisque cela se produira, lorsque le programme n'est pas mal formé. Par conséquent, il verra que, dans tous les cas où le programme n'est pas mal formé, la spécialisation partielle ne se spécialise pas et, en tant que telle, elle vous dira qu'elle est mal formée. (C'est ce que fait Clang).

Je ne pense pas que le compilateur soit obligé de le faire. Et si ce n'est pas le cas, et pense simplement "Ok, sfinae_v_tc'est un type quelconque", alors il n'est pas évident que ce soit une redéclaration. Je pense donc que jusqu'à ce que nous instancions l'un d'eux, il n'y a rien de mal à ne pas lancer d'erreur.

Mais lorsque nous l'instancions, il devrait y avoir le problème que nous avons une redéclaration ou que le programme est mal formé en raison de std::enable_ifl'argument du modèle. GCC devrait en prendre au moins un mais ne fait ni l'un ni l'autre.

Cela ne s'applique également absolument pas à l'exemple le plus facile sans std::enable_if. Donc, je pense toujours que c'est un bug dans GCC, mais je suis suffisamment stupéfait pour ne plus pouvoir le dire avec certitude. Je dirais simplement que quelqu'un devrait signaler cela comme un bug et laisser les gens de gcc y réfléchir.

ANCIEN POST

Il s'agit d'un bogue dans gcc. La norme nous donne des règles pour convertir un modèle de classe en modèles de fonction. Un modèle de classe est plus spécialisé qu'un autre si sa fonction précède celle de l'autre dans l'ordre des modèles de fonction partielle.

J'ai créé les fonctions ici et maintenant gcc affirme que les appeler est ambigu, donc il faudrait aussi dire que les modèles de classe sont également spécifiés.

Remarque: En lisant attentivement la norme, le compilateur dans ma tête est d'accord avec clang.

n314159
la source
Sont sfinae_v_t<T, std::is_integral_v<T>>et sont sfinae_v_t<T, !std::is_integral_v<T>>traités comme les mêmes types? Sémantiquement, ils ne le sont pas.
OFO
@GuillaumeRacicot Très probablement, mais j'aimerais comprendre pourquoi exactement. Par exemple, la norme indique également que "les noms dépendants ne peuvent pas être vérifiés lors de la déclaration de la spécialisation partielle, mais seront vérifiés lors du remplacement dans la spécialisation partielle". Cela ne signifie-t-il pas qu'il s'agit du même type à déterminer après substitution de T dans la spécialisation partielle puisque sfinae_v_t<T>dépend de T? Dans ce cas, ils ne seraient pas les mêmes car l'un ou l'autre serait mal formé.
OFO
@ofo je dois dire que je ne suis pas sûr. C'est un peu fou de penser à ces deux-là, car l'un d'eux ne sera jamais un type et les utiliser tous les deux dans un contexte non-modèle entraînera une erreur de compilation en raison de la enable_if_t. Ma meilleure lecture de la norme est qu'elle n'a pas d'importance si elles sont identiques ou non. Pour la commande partielle, nous comparerons toujours la forme du paramètre de modèle d'une fonction à la forme d'argument modèle de l'autre (c'est-à-dire qu'elle intest déjà substituée) et puis il y a un type réel dans l'une d'entre elles, donc nous n'avons pas à comparer les abstraitement.
n314159
1
En creusant plus profondément, j'ai trouvé ça d' ici . SFINAE devrait fonctionner correctement avec les alias de modèle, sinon template<bool B, typename T> enable_if_t = typename enable_if<B, T>::type;cela ne fonctionnerait pas non plus. Je vais aller de l'avant et déposer le bogue contre gcc, mais je ne sais pas vraiment si gcc se trompe. Merci.
OFO