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ę!!!
c++
templates
language-lawyer
Zmienny efekt uboczny
źródło
źródło
struct B
instancjiA
zB
?B
jest niekompletny ... Ale nie wiadomo, kiedy należy utworzyć instancję członka.Odpowiedzi:
Uważam, że zasadniczo sprowadzają się one do [temp.inst] / 2 ( wyróżnienie moje):
i [temp.inst] / 9
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
Tworzysz instancję
A<B>
. Ale tworzenie instancjiA<B>
tylko tworzy deklaracje, a nie definicje elementów danych statycznych.VB
nigdy nie jest używany w sposób, który wymagałby definicji. Kompilator powinien zaakceptować ten kod.Fragment nr 2
Jak wskazał Jarod42, deklaracja
AB
zawiera 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
Typ klasy jest kompletny tylko w punkcie, w którym osiągniesz zamknięcie
}
specyfikatora klasy [class.mem] / 6 . W związku z tymB
jest niekompletny podczas domyślnej instancjiA<B>
wszystkich fragmentów kodu. Po prostu było to nieistotne dla Snippet # 1. W Snippet # 2 clang spowodował błądNo 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…źródło
constexpr
danych statycznych był tylko deklaracją. C ++ 17 zyskałinline
zmienne iconstexpr
implikujeinline
, a to sprawia, że deklaracja członka statycznych danych w ciele jest definicją.auto
przypadku 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.