Clang nie kompiluje kodu, ale skompilowały go gcc i msvc

14

Nie rozumiem, na czym polega problem: ani w moim kodzie, ani w kompilatorze (mniej możliwe). Jest taki kod:

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

Kompilują go zarówno GCC, jak i MSVC. Przetestowałem go na Godbolt z inną wersją GCC i MSVC 17 (lokalna) i 19. Oto link: https://godbolt.org/z/Enfm6L .

Ale Clang go nie kompiluje i emituje błąd:

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

I jestem zainteresowany - może jest jakaś część standardu, w której ten fragment kodu jest niepoprawny, a może coś innego.

Andrei
źródło
W jaki sposób „std :: set :: reverse_iterator” i „std :: set :: dummy_iterator” są zdefiniowane w nagłówkach clang?
mvidelgauz
std :: set :: dummy_iterator w ogóle nie jest zdefiniowany w nagłówkach clang (mam nadzieję). Możesz zmienić dummy_iterator na cokolwiek zechcesz i nie zmieni to wyniku, ponieważ problem nie występuje w definicji, jak pokazano poniżej.
Andrei
Dziękuję Andrei, przeczytałem odpowiedź i jest to naprawdę interesujące
mvidelgauz

Odpowiedzi:

9

Jest to bardzo prawdopodobne w odniesieniu do CWG 1558 .

Traktowanie nieużywanych argumentów w specjalizacji szablonu aliasu nie jest określone w obecnym brzmieniu 17.6.7 [temp.alias]. Na przykład:

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

Czy odniesienie do first_of z T jest int równoważne po prostu void, czy też jest to niepowodzenie substytucji?

Jest to wada, która została już rozwiązana, ale jeśli używana wersja Clanga nie implementuje jeszcze poprawki, może nadal uważać obie specjalizacje za po prostu zdefiniowanie drugiego argumentu jako void, i nie wykonanie całej spiel niepowodzeń podmiany. Obejściem tego problemu jest nie używanie zwykłego aliasu std::void_t, ale zamiast tego nieco bardziej złożona wersja

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

W przypadku szablonu klasy (który teraz oznacza alias) zdefiniowano błąd podstawienia. Podłączenie tego do twojego przykładu uspokaja Clanga https://godbolt.org/z/VnkwsM .

StoryTeller - Unslander Monica
źródło
1
Innym Rozwiązaniem byłoby stworzenie cechy dla każdego wymagania, a następnie połączyć je w sposób enable_ifz std::disjunction(lub wymaga klauzuli)
AndyG
Dziękujemy za pomoc i referencje! Przykro mi z powodu tego rodzaju błędu w Clang. Zabawne, ale myślałem, że twoja implementacja void_t jest standardem. Nie można przyjąć idei aliasingu szablonów.
Andrei