SFINAE mit VoidT mit unterschiedlichen Compilern führt zu unterschiedlichen Ergebnissen

10

Betrachten Sie den folgenden Code:

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

Wenn ich diesen Code mit g ++ - 4.8.5 kompiliere, wird folgende Fehlermeldung angezeigt:

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

Wenn ich jedoch mit g ++ - 8.3 kompiliere (z. B. in ideone), wird der Code kompiliert und die verschiedenen Spezialisierungen werden korrekt behandelt. War dies ein Fehler in GCC, der behoben wurde, oder rufe ich irgendwie undefiniertes Verhalten auf (und daher ist der Unterschied im Compiler-Verhalten ein strittiger Punkt - es ist undefiniert)?

user11923373
quelle

Antworten:

9

War dies ein Fehler in GCC, der behoben wurde?

Es war ein Defekt im Standard. Es wurde rückwirkend für frühere Standardversionen behoben, aber natürlich haben nur neuere Compilerversionen das Update. Es war die CWG-Ausgabe 1558 , und um daraus zu zitieren:

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?

Die Problemumgehung für Compiler ohne DR-Fix besteht darin, einen Helfer zu verwenden:

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

Ein Substitutionsfehler wird in einer Klassenvorlage garantiert.

Geschichtenerzähler - Unslander Monica
quelle
1
Rückwirkende Korrekturen stören mich. Es bedeutet, dass es niemals ein kanonisches Dokument gibt, das eine Version der Sprache beschreibt.
Leichtigkeitsrennen im Orbit
2
@ LightnessRacesinOrbit - Ich verstehe deinen Standpunkt. Man kann hoffen, dass solche rückwirkenden Korrekturen nur für gültige Konstrukte reserviert sind, die nicht zurückgewiesen werden sollten, so dass der Schaden minimal ist.
StoryTeller - Unslander Monica
@ StoryTeller In der Tat.
Leichtigkeitsrennen im Orbit