Pourquoi mes expressions SFINAE ne fonctionnent-elles plus avec GCC 8.2?

J’ai récemment mis à niveau GCC vers la version 8.2 et la plupart de mes expressions SFINAE ont cessé de fonctionner.

Ce qui suit est quelque peu simplifié, mais illustre le problème:

#include  #include  class Class { public: template < typename U, typename std::enable_if< std::is_const<typename std::remove_reference::type>::value, int >::type... > void test() { std::cout << "Constant" << std::endl; } template < typename U, typename std::enable_if< !std::is_const<typename std::remove_reference::type>::value, int >::type... > void test() { std::cout << "Mutable" << std::endl; } }; int main() { Class c; c.test(); c.test(); return 0; } 

C ++ (gcc) – Essayez-le en ligne

C ++ (clang) – Essayez-le en ligne

Les anciennes versions de GCC (malheureusement, je ne me souviens plus de la version exacte que j’avais précédemment installée) ainsi que Clang comstacknt parfaitement le code ci-dessus, mais GCC 8.2 génère une erreur indiquant:

  : En fonction 'int main ()':
 : 29: 19: erreur: l'appel de 'test ()' surchargé est ambigu
      c.test ();
                    ^
 : 12: 10: note: candidat: 'void Class :: test () [avec U = int &;  nom_type std :: enable_if :: type> :: valeur> :: type ... = {}] '
      void test () {
           ^ ~~~
 : 22: 10: note: candidat: 'void Class :: test () [avec U = int &;  nom_type std :: enable_if :: type> :: valeur)> :: type ... = {}] '
      void test () {
           ^ ~~~
 : 30: 25: erreur: l'appel de 'test ()' surchargé est ambigu
      c.test ();
                          ^
 : 12: 10: note: candidat: 'void Class :: test () [avec U = const int &;  nom_type std :: enable_if :: type> :: valeur> :: type ... = {}] '
      void test () {
           ^ ~~~
 : 22: 10: remarque: candidat: 'void Class :: test () [avec U = const int &;  nom_type std :: enable_if :: type> :: valeur)> :: type ... = {}] '
      void test () { 

Comme d’habitude lorsque différents compilateurs et versions de compilateur traitent le même code différemment, je suppose que j’appelle un comportement indéfini. Que dit la norme sur le code ci-dessus? Qu’est-ce que je fais mal?


Remarque: la question ne concerne pas les moyens de résoudre ce problème, mais plusieurs vous viennent à l’esprit. La question est de savoir pourquoi cela ne fonctionne pas avec GCC 8 – est-ce que le standard ne le définit pas, ou s’agit-il d’un bogue du compilateur?

Note 2: Puisque tout le monde sautait sur le type de défaut par défaut de std::enable_if , j’ai changé la question pour utiliser int place. Le problème demeure.

Note 3: Rapport de bogue GCC créé

Ceci est mon sharepoint vue sur elle. En bref, clang a raison et gcc a une régression.

Nous avons selon [temp.deduct] p7 :

La substitution se produit dans tous les types et expressions utilisés dans le type de fonction et dans les déclarations de paramètre de modèle. […]

Cela signifie que la substitution doit avoir lieu que le pack soit vide ou non. Comme nous sums toujours dans le contexte immédiat, cela est possible avec SFINAE.

Ensuite, nous avons qu’un paramètre variadic est en effet considéré comme un paramètre de modèle réel; de [temp.variadic] p1

Un modèle de pack de parameters est un paramètre de modèle qui accepte zéro ou plusieurs arguments de modèle.

et [temp.param] p2 indique quels parameters de modèle non typés sont autorisés:

Un paramètre de modèle non typé doit avoir l’un des types suivants (éventuellement qualifiés de cv):

  • un type littéral, ayant une forte égalité structurelle ([class.compare.default]), aucun sous-object modifiable ou volatil et dans lequel, s’il existe un opérateur de membre par défaut <=>, il est déclaré public,

  • un type de référence lvalue,

  • un type qui contient un type d’espace réservé ([dcl.spec.auto]), ou

  • un espace réservé pour un type de classe déduit ([dcl.type.class.deduct]).

Notez que void ne correspond pas à la facture, votre code (tel que posté) est mal formé.

Je ne suis pas juriste des langues, mais la citation suivante ne peut-elle pas être reliée au problème?

[temp.deduct.type / 9] : Si Pi est une extension de pack, le modèle de Pi est comparé à chaque argument restant dans la liste d’arguments de modèle de A. Chaque comparaison déduit des arguments de modèle pour les positions suivantes dans les packs de parameters de modèle développés. par Pi.

Il me semble que puisqu’il n’y a plus d’ argument dans la liste d’arguments de modèle , il n’y a pas de comparaison du motif (qui contient enable_if ). S’il n’y a pas de comparaison, alors il n’y a pas non plus de déduction et la substitution se produit après la déduction, je crois. Par conséquent, s’il n’y a pas de substitution, aucun SFINAE n’est appliqué.

S’il vous plait corrigez moi si je me trompe. Je ne suis pas sûr que ce paragraphe s’applique ici, mais il existe davantage de règles similaires concernant le développement de pack dans [temp.deduct]. En outre, cette discussion peut aider une personne plus expérimentée à résoudre le problème dans son intégralité: https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/JwZiV2rrX1A .

Réponse partielle: utilisez typename = typename enable_if<...>, T=0 avec différents T s:

 #include  #include  class Class { public: template < typename U, typename = typename std::enable_if_t< std::is_const::type>::value >, int = 0 > void test() { std::cout << "Constant" << std::endl; } template < typename U, typename = typename std::enable_if_t< !std::is_const::type>::value >, char = 0 > void test() { std::cout << "Mutable" << std::endl; } }; int main() { Class c; c.test(); c.test(); return 0; } 

( démo )

Toujours essayer de comprendre ce que diable fait std::enable_if<...>::type... signifie connaître le type par défaut est void .