Clang non compila il codice ma gcc e msvc lo hanno compilato


14

Non capisco qual è il problema: né nel mio codice né nel compilatore (meno possibile). C'è un pezzo di codice come questo:

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

Sia GCC che MSVC lo compilano. L'ho provato su godbolt con diverse versioni di GCC e MSVC 17 (locale) e 19. Ecco un link: https://godbolt.org/z/Enfm6L .

Ma Clang non lo compila ed emette un errore:

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

E sono interessato - forse c'è qualche parte dello standard in cui questo pezzo di codice è errato o forse qualcos'altro.


Come vengono definiti "std :: set :: reverse_iterator" e "std :: set :: dummy_iterator" nelle intestazioni di clang?
mvidelgauz,

std :: set :: dummy_iterator non è affatto definito nelle intestazioni di clang (spero). Puoi cambiare dummy_iterator in qualsiasi cosa tu voglia e non cambierà il risultato poiché il problema non è nella definizione come mostrato di seguito.
Andrei,

Grazie Andrei, ho letto la risposta ed è davvero interessante
mvidelgauz,

Risposte:


9

Ciò è molto probabilmente correlato a CWG 1558 .

Il trattamento degli argomenti non utilizzati in una specializzazione di modello alias non è specificato dalla dicitura attuale del 17.6.7 [alias temp.]. Per esempio:

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

Il riferimento a first_of con T è int equivalente a semplicemente nullo o è un errore di sostituzione?

È un difetto che è stato risolto da allora, ma se la versione di Clang che hai usato non implementa ancora la correzione, potrebbe comunque considerare entrambe le specializzazioni semplicemente definendo il secondo argomento voide non eseguendo l'intero errore di sostituzione. La soluzione è di non utilizzare un semplice alias std::void_t, ma una versione leggermente più complessa

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

Per un modello di classe (che rappresenta ora l'alias), viene definito l'errore di sostituzione. Inserendolo nel tuo esempio si calma Clang https://godbolt.org/z/VnkwsM .


1
Un'altra soluzione alternativa sarebbe quella di creare tratti per ogni requisito e poi combinarli in una ( enable_ifcon std::disjunctionuna clausola
obbligatoria

Grazie per l'aiuto e la consultazione! Triste sapere di questo tipo di bug a Clang. Divertente, però, ho pensato che la tua implementazione di void_t sia standard. Impossibile adottare l'idea di alias dei modelli.
Andrei,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.