SFINAE utilizzando VoidT con compilatori diversi porta a risultati diversi


10

Considera il seguente codice:

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

Usando g ++ - 4.8.5, la compilazione di questo codice mi dà il seguente messaggio di errore:

~/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>’

Tuttavia, quando compilo usando g ++ - 8.3 (in, per esempio, ideone) il codice viene compilato e le diverse specializzazioni vengono trattate correttamente. È stato corretto un bug in GCC o sto in qualche modo invocando un comportamento indefinito (e quindi la differenza nel comportamento del compilatore è un punto controverso - non è definito)?

Risposte:


9

È stato corretto un errore in GCC?

Era un difetto nello standard. È stato risolto in modo retroattivo per le versioni standard precedenti, ma ovviamente solo le versioni più recenti del compilatore avranno la correzione. Era il numero 1558 di CWG , e per citare da esso:

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?

La soluzione alternativa per i compilatori senza la correzione DR consiste nell'utilizzare un helper:

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

L'errore di sostituzione è garantito in un modello di classe.


1
Le correzioni retroattive mi infastidiscono. Significa che non c'è mai un documento canonico che descriva una versione della lingua.
Corse di leggerezza in orbita,

2
@LightnessRacesinOrbit - Vedo il tuo punto. Si può sperare che tali correzioni retroattive siano riservate solo a costrutti validi che non dovrebbero essere respinti, quindi il danno è minimo.
StoryTeller - Unslander Monica

@StoryTeller In effetti.
Razze di leggerezza in orbita,
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.