SFINAE utilisant VoidT avec différents compilateurs conduit à des résultats différents


10

Considérez le code suivant:

template <typename T> using VoidT = void;

class A {
public:
   using TEST = int;
};

class C {
public:
   using DIFFERENT = int;
};

template <typename T, typename Enable = void>
class B {
public:
   B() = delete;
};

template <typename T>
class B<T, VoidT<typename T::TEST>> {
public:
   B() = default;
};

template <typename T>
class B<T, VoidT<typename T::DIFFERENT>> {
public:
   B() = default;
};

int main() {
   B<A> a;
   B<C> b;

   return 0;
}

En utilisant g ++ - 4.8.5, la compilation de ce code me donne le message d'erreur suivant:

~/test/compile_test> g++ -std=c++11 test.cpp

test.cpp:31:7: error: redefinition of ‘class B<T, void>’

test.cpp:24:7: error: previous definition of ‘class B<T, void>’

Cependant, lorsque je compile en utilisant g ++ - 8.3 (dans, par exemple, ideone), le code se compile et les différentes spécialisations sont traitées correctement. Était-ce un bug dans GCC qui a été corrigé, ou suis-je en quelque sorte invoquant un comportement non défini (et donc la différence de comportement du compilateur est un point discutable - il n'est pas défini)?

Réponses:


9

Était-ce un bug dans GCC qui a été corrigé?

C'était un défaut dans la norme. Il a été corrigé rétroactivement pour les versions standard précédentes, mais bien sûr, seules les versions de compilateur plus récentes auront le correctif. C'était CWG Issue 1558 , et pour en citer:

Le traitement des arguments inutilisés dans une spécialisation de modèle d'alias n'est pas spécifié par le libellé actuel de 17.6.7 [temp.alias]. Par exemple:

  #include <iostream>

  template <class T, class...>
    using first_of = T;

  template <class T>
    first_of<void, typename T::type> f(int)
      { std::cout << "1\n"; }

  template <class T>
    void f(...)
      { std::cout << "2\n"; }

  struct X { typedef void type; };

  int main() {
    f<X>(0);
    f<int>(0);
  }

La référence à first_of avec T étant int équivalente à simplement void, ou est-ce un échec de substitution?

La solution de contournement pour les compilateurs sans le correctif DR est d'utiliser un assistant:

template<typename T> struct voider { using type = void; };
template <typename T> using VoidT = typename voider<T>::type;

L'échec de substitution est garanti dans un modèle de classe.


1
Les correctifs rétroactifs me dérangent. Cela signifie qu'il n'y a jamais de document canonique décrivant une version du langage.
Courses de légèreté en orbite du

2
@LightnessRacesinOrbit - Je vois votre point. On peut espérer que ces correctifs rétroactifs sont réservés uniquement aux constructions valides qui ne doivent pas être rejetées, donc le préjudice est minime.
Conteur - Unslander Monica

@StoryTeller En effet.
Courses de légèreté en orbite
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.