J'ai rencontré ce problème en essayant de spécialiser tuple_size
/ tuple_element
pour 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;
}
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?
gcc
compile, vu qu'il ne compile pas ça ...Réponses:
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 toujoursT
, 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_t
c'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_if
l'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.
la source
sfinae_v_t<T, std::is_integral_v<T>>
et sontsfinae_v_t<T, !std::is_integral_v<T>>
traités comme les mêmes types? Sémantiquement, ils ne le sont pas.sfinae_v_t<T>
dépend deT
? Dans ce cas, ils ne seraient pas les mêmes car l'un ou l'autre serait mal formé.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'elleint
est 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.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.