Próbowanie zrozumienia szablonów i wyszukiwania nazw

9

Próbuję zrozumieć następujące fragmenty kodu

Fragment nr 1

template <typename T>
struct A
{
    static constexpr int VB = T::VD;
};

struct B : A<B>
{
};

Ani gcc9, ani clang9 nie generują tutaj błędu.

P: Dlaczego ten kod się kompiluje? Czy nie A<B>dziedziczymy podczas dziedziczenia po B? W B nie ma VD, więc czy kompilator nie powinien tutaj zgłaszać błędu?

Fragment nr 2

template <typename T>
struct A
{
    static constexpr auto AB = T::AD; // <- No member named AD in B
};

struct B : A<B>
{
    static constexpr auto AD = 0xD;
};

W tym przypadku gcc9 kompiluje się dobrze, ale clang9 zgłasza błąd „Nie ma członka o nazwie AD w B”.

P: Dlaczego kompiluje się z gcc9 / dlaczego nie kompiluje się z clang9?

Fragment nr 3

template <typename T>
struct A
{
    using TB = typename T::TD;
};

struct B : A<B>
{
    using TD = int;
};

Tutaj zarówno clang9, jak i gcc9 generują błąd. gcc9 mówi „nieprawidłowe użycie niekompletnego typu„ struct B ””.

P: Jeśli struktura B jest tutaj niekompletna, to dlaczego nie jest niekompletna we fragmencie nr 2?

Używane flagi kompilatora: -std=c++17 -O3 -Wall -Werror. Z góry dziękuję!!!

Zmienny efekt uboczny
źródło
@xception nie Czy struct Binstancji Az B?
Zmienny efekt
clang9 zgłasza błąd: „Brak członka o nazwie AD w B” . ponieważ Bjest niekompletny ... Ale nie wiadomo, kiedy należy utworzyć instancję członka.
Jarod42,
@MutableSideEffect o tak, mój zły, przeczytaj to również jako szablon :(
xception
@ Jarod42, dlaczego gcc kompiluje się dobrze?
Zmienny efekt
1
Oznacziłem to pytanie jako „wymagające większej uwagi”, a pytanie to rzeczywiście zawiera więcej niż jedno pytanie (stąd mój wniosek), więc dlaczego moja flaga jest błędna?
Dominique

Odpowiedzi:

4

Uważam, że zasadniczo sprowadzają się one do [temp.inst] / 2 ( wyróżnienie moje):

Domniemana instancji o specjalizacji klasa szablonu powoduje niejawne konkretyzacji deklaracji, ale nie z definicji , domyślnych argumentów lub noexcept-specyfikatorami tych funkcji składowych klasy, klas członkowskich, scoped wyliczeń członkowskich, statycznymi członkami danych , szablony członkowskich, a przyjaciele; […]

i [temp.inst] / 9

Implementacja nie może domyślnie tworzyć instancji […] statycznego elementu danych szablonu klasy […], chyba że taka instancja jest wymagana.

Sformułowanie w standardzie dotyczące domyślnej instancji szablonu pozostawia wiele szczegółów do interpretacji. Ogólnie wydaje mi się, że po prostu nie można polegać na tym, że części szablonu nie są tworzone, chyba że specyfikacja wyraźnie to mówi. A zatem:

Fragment nr 1

P: Dlaczego ten kod się kompiluje? Czy nie dziedziczymy A, gdy dziedziczymy po B? W B nie ma VD, więc czy kompilator nie powinien tutaj zgłaszać błędu?

Tworzysz instancję A<B>. Ale tworzenie instancji A<B>tylko tworzy deklaracje, a nie definicje elementów danych statycznych. VBnigdy nie jest używany w sposób, który wymagałby definicji. Kompilator powinien zaakceptować ten kod.

Fragment nr 2

P: Dlaczego kompiluje się z gcc9 / dlaczego nie kompiluje się z clang9?

Jak wskazał Jarod42, deklaracja ABzawiera typ zastępczy. Wydaje mi się, że brzmienie standardu nie jest do końca jasne, co ma się tu wydarzyć. Czy utworzenie wystąpienia deklaracji statycznego elementu danych zawierającego typ symbolu zastępczego wywołuje odliczenie typu symbolu zastępczego, a zatem stanowi zastosowanie wymagające definicji elementu danych statycznych? Nie mogę znaleźć sformułowania w standardzie, które jednoznacznie powiedziałoby na to „tak” lub „nie”. Tak więc powiedziałbym, że obie interpretacje są tutaj równie ważne, a zatem GCC i clang mają rację…

Fragment nr 3

P: Jeśli struktura B jest tutaj niekompletna, to dlaczego nie jest niekompletna we fragmencie nr 2?

Typ klasy jest kompletny tylko w punkcie, w którym osiągniesz zamknięcie }specyfikatora klasy [class.mem] / 6 . W związku z tym Bjest niekompletny podczas domyślnej instancji A<B>wszystkich fragmentów kodu. Po prostu było to nieistotne dla Snippet # 1. W Snippet # 2 clang spowodował błąd No member named AD in B. Podobnie jak w przypadku fragmentu nr 2, nie mogę znaleźć sformułowania, w którym wystąpiłaby dokładnie instancja deklaracji aliasu członka. Jednak w przeciwieństwie do definicji statycznych elementów danych, nie ma sformułowania, które wyraźnie zapobiegałoby tworzeniu instancji deklaracji aliasu elementów podczas niejawnej instancji szablonu klasy. Powiedziałbym zatem, że zachowanie zarówno GCC, jak i clang jest prawidłową interpretacją standardu w tym przypadku…

Michael Kenzel
źródło
Dzięki. W takim przypadku inicjalizujemy element danych statycznych w ciele. Czy inicjalizacja jest częścią deklaracji, czy też częścią definicji statycznego elementu danych? Miałem wrażenie, że jeśli inicjalizacja jest w ciele, to jest to część deklaracji elementu danych statycznych. Jeśli jest to część deklaracji, twoja pierwsza oferta wymaga natychmiastowej instancji jako części otaczającej domyślnej instancji szablonu klasy.
Johannes Schaub - litb
Zajrzałem do specyfikacji i okazało się, że jest tutaj różnica między C ++ 14 i C ++ 17. W C ++ 14 element constexprdanych statycznych był tylko deklaracją. C ++ 17 zyskał inlinezmienne i constexprimplikuje inline, a to sprawia, że ​​deklaracja członka statycznych danych w ciele jest definicją.
Johannes Schaub - litb
eel.is/c++draft/dcl.spec.auto#4.sentence-2 mówi: „Typ zmiennej zadeklarowanej za pomocą typu symbolu zastępczego jest wywnioskowany z jego inicjalizatora. Takie użycie jest dozwolone w deklaracji inicjującej ([dcl. init]) zmiennej. ”. Dlatego argumentowałbym, że definicja statycznego elementu danych jest wymagana, ponieważ zawiera on inicjator.
Johannes Schaub - litb
@ JohannesSchaub-litb Dziękujemy za przyjrzenie się temu! Zastanawiałem się również nad tymi pytaniami, ale nie znalazłem sformułowania, które byłoby rozstrzygające. Jeśli chodzi o inicjalizację vs. definicję, rozważ definicję funkcji elementu w definicji szablonu klasy. Taka definicja jest również deklaracją i nie ma innych deklaracji. Jednak domyślna instancja szablonu klasy utworzy tylko deklarację, ale nie definicję funkcji elementu. Dlaczego to samo nie jest prawdą w przypadku elementów danych statycznych?
Michael Kenzel,
jeśli nie ma nic, co wymaga definicji, definicja nie jest tworzona. Ale w tym autoprzypadku reguła mówi, że deklaracja musi być deklaracją inicjującą. Może się tak zdarzyć tylko wtedy, gdy wiadomo, że deklaracja jest definicją (o ile mi wiadomo ... przez jakiś czas byłem poza krajem prawników). W przeszłości był podobny przypadek i dodawano eel.is/c++draft/temp.inst#2.sentence-3 , gdzie deklaracja będąca definicją jest tworzona jako „znana jako definicja” bez faktycznego tworzenie definicji.
Johannes Schaub - litb