Czy Clang słusznie odrzuca kod, w którym zagnieżdżona klasa szablonu klasy jest definiowana tylko poprzez specjalizacje?

17

Biorąc pod uwagę następujący szablon klasy:

template<typename T>
struct Outer
{
    struct Inner;

    auto f(Inner) -> void;
};

definiujemy Innerosobno dla każdej specjalizacji Outer:

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

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

a następnie raz zdefiniuj funkcję członka fdla wszystkich specjalizacji Outer:

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

}

ale Clang (9.0.0) narzeka:

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

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

                      ^

Możemy uniknąć błędu kompilatora, podając również definicję Innerwszystkich innych specjalizacji Outer:

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

lub definiując fosobno dla każdej specjalizacji:

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

}

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

}

Zarówno GCC, jak i MSVC akceptują kod początkowy, co nasuwa pytanie; czy jest to błąd Clanga, czy jest to jedyna zgodna implementacja spośród wszystkich trzech?

Wypróbuj Eksplorator kompilatorów

wypaczony
źródło
Specjalizacje Wewnętrzne są nieistotne, ich usunięcie nie zmienia wyniku kompilacji.
n. „zaimki” m.
@ n.'Pronouns'm. Nie jestem pewny co masz na myśli. Zarówno dodanie definicji Innerdla wszystkich innych specjalizacji, jak i zdefiniowanie fosobno dla każdej specjalizacji rozwiązuje błąd kompilacji.
zepsute
Przeczytajmy to jeszcze raz: usunięcie ich nie zmienia wyniku kompilacji . Nie dodawanie, usuwanie. gcc clang
n. „zaimki” m.
@ n.'Pronouns'm. Rozumiem teraz, co masz na myśli, ale nadal jest to dziwny komentarz. Chodzi mi o to, że Innerjest zgłaszany jako niekompletny typ, pomimo definicji dla każdej specjalizacji Outer. Wyraźnie Innerbędzie (poprawnie) niekompletny, jeśli usuniesz jego definicje.
zirytowany
„Wyraźnie, że Wewnętrzna będzie (poprawnie) niekompletnym typem, jeśli usuniesz jej definicję (definicje).” Nie, to wcale nie jest ckear. Specjalizacja jest całkowicie oddzielnym szablonem i nie wpływa w ogóle na główny szablon.
n. „zaimki” m.

Odpowiedzi:

4

Uważam, że Clang nie zgadza się z odrzuceniem twojego kodu. Musimy zadać sobie pytanie, jak wypada twoja deklaracja funkcji i definicja

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

// ...

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

W tym przykładzie T::Innerjest oczywiście typ zależny. Dlatego Clang może nie założyć, że jest niekompletny do momentu utworzenia instancji. Czy to samo dotyczy twojego przykładu? Tak bym powiedział. Ponieważ mamy to w standardzie:

[temp.dep.type]

5 Nazwa jest członkiem bieżącej instancji, jeśli tak jest

  • Niewykwalifikowana nazwa, która, gdy spojrzy się w górę, odnosi się do co najmniej jednego członka klasy, która jest bieżącą instancją lub jej niezależną klasą podstawową. [Uwaga: Może się to zdarzyć tylko podczas wyszukiwania nazwy w zakresie objętym definicją szablonu klasy. - uwaga końcowa]
  • ...

Nazwa jest zależnym elementem bieżącej instancji, jeśli jest ona częścią bieżącej instancji, która, gdy jest sprawdzana, odnosi się do co najmniej jednego elementu klasy, która jest bieżącą instancją.

9 Typ jest zależny, jeśli tak jest

  • ...
  • członek nieznanej specjalizacji,
  • zagnieżdżona klasa lub wyliczenie, które jest zależnym elementem bieżącej instancji,
  • ...

Zatem pierwszy punkt w paragrafie 9 dotyczy sprawy typename T::Inner. To jest typ zależny.

Tymczasem twoja sprawa jest objęta drugim pociskiem. Outer::Innerto nazwa, która znajduje się w bieżącej instancji Outer, ponadto znajduje się w Outersobie, a nie w klasie bazowej. To czyni go zależnym członkiem bieżącej instancji. Ta nazwa odnosi się do zagnieżdżonej klasy. Co oznacza, że ​​obowiązują wszystkie warunki zawarte w drugim pocisku, a zatem Outer::Innerrównież typ zależny!

Ponieważ w obu przypadkach mamy typ zależny, kompilatory powinny traktować je jednakowo jako typy zależne. Mój wniosek jest taki, że GCC i MSVC mają rację.

StoryTeller - Unslander Monica
źródło
Zgłoszony błąd . Dzięki.
invexed