Clang kompiliert keinen Code, aber gcc und msvc haben ihn kompiliert

14

Ich verstehe nicht, was das Problem ist: entweder in meinem Code oder im Compiler (weniger möglich). Es gibt einen Code wie diesen:

#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;
}

Sowohl GCC als auch MSVC kompilieren es. Ich habe es auf Godbolt mit verschiedenen Versionen von GCC und MSVC 17 (lokal) und 19 getestet. Hier ist ein Link: https://godbolt.org/z/Enfm6L .

Aber Clang kompiliert es nicht und gibt einen Fehler aus:

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

Und ich bin interessiert - vielleicht gibt es einen Teil des Standards, in dem dieser Code falsch ist, oder vielleicht etwas anderes.

Andrei
quelle
Wie werden "std :: set :: reverse_iterator" und "std :: set :: dummy_iterator" in Clang-Headern definiert?
mvidelgauz
std :: set :: dummy_iterator ist in clang-Headern überhaupt nicht definiert (ich hoffe). Sie können dummy_iterator nach Belieben ändern und das Ergebnis wird nicht geändert, da das Problem nicht in der unten gezeigten Definition enthalten ist.
Andrei
Danke Andrei, ich habe die Antwort gelesen und es ist in der Tat interessant
mvidelgauz

Antworten:

9

Dies hängt sehr wahrscheinlich mit CWG 1558 zusammen .

Die Behandlung nicht verwendeter Argumente in einer Alias-Vorlagenspezialisierung ist im aktuellen Wortlaut von 17.6.7 [temp.alias] nicht festgelegt. Zum Beispiel:

  #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);
  }

Entspricht der Verweis auf first_of mit T int einfach void oder handelt es sich um einen Substitutionsfehler?

Es ist ein Fehler, der seitdem behoben wurde, aber wenn die von Ihnen verwendete Version von Clang das Update noch nicht implementiert, werden möglicherweise beide Spezialisierungen als einfache Definition des zweiten Arguments betrachtet voidund nicht als Spiel für den gesamten Substitutionsfehler. Die Problemumgehung besteht darin, keinen einfachen Alias ​​zu verwenden std::void_t, sondern eine etwas komplexere Version

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

Für eine Klassenvorlage (wofür der Alias ​​jetzt steht) wird der Substitutionsfehler definiert. Wenn Sie dies in Ihr Beispiel einfügen, wird Clang https://godbolt.org/z/VnkwsM beschwichtigt .

Geschichtenerzähler - Unslander Monica
quelle
1
Eine andere Problemumgehung wäre, Merkmale für jede Anforderung zu erstellen und sie dann in einer enable_ifwith std::disjunction(oder einer
require-
Vielen Dank für Hilfe und Hinweis! Traurig über diese Art von Fehler in Clang zu hören. Komisch fand ich jedoch, dass Ihre Implementierung von void_t Standard ist. Die Idee des Vorlagen-Aliasing kann nicht übernommen werden.
Andrei