Ist Clang korrekt, um Code abzulehnen, in dem die verschachtelte Klasse einer Klassenvorlage nur über Spezialisierungen definiert ist?

17

Gegeben die folgende Klassenvorlage:

template<typename T>
struct Outer
{
    struct Inner;

    auto f(Inner) -> void;
};

Wir definieren Innerseparat für jede Spezialisierung von Outer:

template<>
struct Outer<int>::Inner {};

template<>
struct Outer<double>::Inner {};

und definieren Sie dann die Elementfunktion feinmal für alle Spezialisierungen von Outer:

auto Outer<T>::f(Inner) -> void
{

}

aber Clang (9.0.0) beschwert sich:

error: variable has incomplete type 'Outer::Inner'

auto Outer<T>::f(Inner) -> void

                      ^

Wir können dem Compilerfehler ausweichen, indem wir auch eine Definition Innerfür alle anderen Spezialisierungen von Outer:

template<typename T>
struct Outer<T>::Inner {};

oder indem ffür jede Spezialisierung separat definiert wird :

template<>
auto Outer<int>::f(Inner) -> void
{

}

template<>
auto Outer<double>::f(Inner) -> void
{

}

Sowohl GCC als auch MSVC akzeptieren den ursprünglichen Code, der die Frage aufwirft. Ist dies ein Clang-Fehler oder ist es die einzige konforme Implementierung von den drei?

Probieren Sie den Compiler Explorer aus

invexiert
quelle
Spezialisierungen von Inner sind irrelevant. Wenn Sie sie entfernen, ändert sich das Kompilierungsergebnis nicht.
n. 'Pronomen' m.
@ n.'pronomen'm. Ich bin mir nicht sicher was du meinst. Sowohl das Hinzufügen einer Definition Innerfür alle anderen Spezialisierungen als auch das fseparate Definieren für jede Spezialisierung beheben den Kompilierungsfehler.
Invexed
Lesen wir es noch einmal: Wenn Sie sie entfernen, ändert sich das Kompilierungsergebnis nicht . Nicht hinzufügen, entfernen. gcc clang
n. 'Pronomen' m.
@ n.'pronomen'm. Ich verstehe, was du jetzt meinst, aber das ist immer noch ein seltsamer Kommentar. Der Punkt meiner Frage war, dass Innertrotz Definitionen für jede Spezialisierung der OuterBereitstellung als unvollständiger Typ gemeldet wird. Es ist klar, Innerdass (korrekt) ein unvollständiger Typ ist, wenn Sie seine Definition (en) entfernen.
Invexed
"Es ist klar, dass Inner (korrekt) ein unvollständiger Typ ist, wenn Sie seine Definition (en) entfernen." Nein, das ist überhaupt nicht klar. Eine Spezialisierung ist eine völlig separate Vorlage und wirkt sich überhaupt nicht auf die Hauptvorlage aus.
n. 'Pronomen' m.

Antworten:

4

Ich glaube, Clang ist falsch, Ihren Code abzulehnen. Wir müssen uns fragen, wie sich Ihre Funktionsdeklaration und -definition verhält

auto f(typename T::Inner) -> void;

// ...

template<typename T>
auto Outer<T>::f(typename T::Inner) -> void
{ }

In diesem Beispiel T::Innerhandelt es sich offensichtlich um einen abhängigen Typ. Daher kann Clang nicht davon ausgehen, dass es bis zur Instanziierung unvollständig ist. Gilt das auch für Ihr Beispiel? So würde ich sagen. Denn wir haben dies im Standard:

[temp.dep.type]

5 Ein Name ist Mitglied der aktuellen Instanziierung, falls dies der Fall ist

  • Ein nicht qualifizierter Name, der sich beim Nachschlagen auf mindestens ein Mitglied einer Klasse bezieht, bei der es sich um die aktuelle Instanziierung oder eine nicht abhängige Basisklasse handelt. [Hinweis: Dies kann nur auftreten, wenn ein Name in einem Bereich nachgeschlagen wird, der von der Definition einer Klassenvorlage eingeschlossen ist. - Endnote]
  • ...

Ein Name ist ein abhängiges Mitglied der aktuellen Instanziierung, wenn es sich um ein Mitglied der aktuellen Instanziierung handelt, das sich beim Nachschlagen auf mindestens ein Mitglied einer Klasse bezieht, bei der es sich um die aktuelle Instanziierung handelt.

9 Ein Typ ist abhängig, wenn dies der Fall ist

  • ...
  • ein Mitglied einer unbekannten Spezialisierung,
  • eine verschachtelte Klasse oder Aufzählung, die ein abhängiges Mitglied der aktuellen Instanziierung ist,
  • ...

Der erste Punkt in Absatz 9 behandelt also den Fall typename T::Inner. Das ist ein abhängiger Typ.

In der Zwischenzeit wird Ihr Fall von der zweiten Kugel abgedeckt. Outer::Innerist ein Name, der in der aktuellen Instanziierung von gefunden wird Outer, außerdem in sich Outerselbst und nicht in einer Basisklasse. Das macht es zu einem abhängigen Mitglied der aktuellen Instanziierung. Dieser Name bezieht sich auf eine verschachtelte Klasse. Dies bedeutet, dass alle Bedingungen im zweiten Aufzählungszeichen gelten, wodurch auch Outer::Innerein abhängiger Typ entsteht!

Da wir in beiden Fällen einen abhängigen Typ haben, sollten Compiler sie als abhängige Typen gleich behandeln. Mein Fazit ist, dass GCC und MSVC Recht haben.

Geschichtenerzähler - Unslander Monica
quelle
Fehler gemeldet . Vielen Dank.
Invexed