Clang ne compile pas de code mais gcc et msvc l'ont compilé

14

Je ne comprends pas quel est le problème: soit dans mon code, soit dans le compilateur (moins possible). Il y a un morceau de code comme celui-ci:

#include <iostream>
#include <type_traits>
#include <set>


template<typename T, typename = void>
struct TestA: std::false_type {};

template<typename T>
struct TestA<T, std::void_t<typename T::reverse_iterator>> : std::true_type {};

template<typename T>
struct TestA<T, std::void_t<typename T::dummy_iterator>> : std::true_type {};

int main()
{
    std::cout << TestA<std::set<int>>::value;
}

GCC et MSVC le compilent. Je l'ai testé sur godbolt avec différentes versions de GCC et MSVC 17 (local) et 19. Voici un lien: https://godbolt.org/z/Enfm6L .

Mais Clang ne le compile pas et émet une erreur:

redefinition of `'TestA<T, std::void_t<typename T::dummy_iterator> >'`

Et je suis intéressé - peut-être qu'il y a une partie de la norme où ce morceau de code est incorrect ou peut-être autre chose.

Andrei
la source
Comment "std :: set :: reverse_iterator" et "std :: set :: dummy_iterator" sont-ils définis dans les en-têtes clang?
mvidelgauz
std :: set :: dummy_iterator n'est pas du tout défini dans les en-têtes clang (j'espère). Vous pouvez remplacer dummy_iterator par tout ce que vous voulez et cela ne changera pas le résultat car le problème n'est pas dans la définition comme indiqué ci-dessous.
Andrei
Merci Andrei, j'ai lu la réponse et c'est intéressant en effet
mvidelgauz

Réponses:

9

Ceci est très probablement lié au CWG 1558 .

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?

C'est un défaut qui a été corrigé depuis, mais si la version de Clang que vous avez utilisée n'implémente pas encore le correctif, il peut toujours considérer les deux spécialisations comme définissant simplement le deuxième argument comme void, et ne faisant pas tout le spiel d'échec de substitution. La solution consiste à ne pas utiliser un alias simple std::void_t, mais plutôt une version légèrement plus complexe

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

Pour un modèle de classe (qui correspond maintenant à l'alias), l'échec de substitution est défini. Brancher cela dans votre exemple apaise Clang https://godbolt.org/z/VnkwsM .

Conteur - Unslander Monica
la source
1
Une autre solution de contournement consisterait à créer des traits pour chaque exigence, puis à les combiner dans une enable_ifavec std::disjunction(ou une clause requiert)
AndyG
Merci pour votre aide et référence! Triste d'entendre parler de ce genre de bogue à Clang. C'est drôle, cependant, je pensais que votre implémentation de void_t est standard. Impossible d'adopter l'idée d'alias de modèle.
Andrei