2016-10-03 11 views
6

Nie mam pojęcia, w jaki sposób kompiluje się CRTP. Jeśli mamy coś takiego:Dlaczego CRTP nie powoduje nieskończonego zagnieżdżania?

template<class T> 
class Base 
{ 

}; 
class Derived : public Base<Derived> 
{ 

}; 

Dlaczego podczas kompilacji nie dzieje się coś podobnego?

(X[Y] oznacza X dziedziczy Y)

Po zgłoszenia o Derived przykład Derived d;

d jest rozszerzany do nieskończonej ponownego szablonów i spadków

d[Base<Derived[Base<Derived[Base<Derived[Base<Derived[...]>]>]>]>]

dlaczego czy to się nie dzieje? Wszystkie tutoriale na temat CRTP wyjaśniają jedynie, co można z nim zrobić, a nie co dzieje się pod maską (przynajmniej niejasno).

+0

W kontekście 'Base',' T' nie jest w pełni kompletnym typem. Z tego powodu nie można uzyskać dostępu do rzeczy takich jak 'typedef's zdefiniowane w' T' z 'Base' (patrz [1] (http://stackoverflow.com/questions/6006614/c-static-polymorphism-crtp- i-using-typedefs-from-derived-classes) i [2] (http://stackoverflow.com/questions/652155/invalid-use-of-incomplete-type)). Ponieważ 'T' nie jest w pełni kompletnym typem, kompilator nie musi w nieskończoność rekurencyjnie występować w tej sytuacji. Przynajmniej to moje zrozumienie. – Cornstalks

+0

@ Eichhörnchen Ponieważ wywodzę się z Base, która jest szablonowana przez Derived, która dziedziczy po Base, która jest szablonowana przez Derived, która dziedziczy po Base ... –

+0

@Cornstalks yea sounds about right –

Odpowiedz

7

Zasadnicza koncepcja do zrozumienia jest to, że wystąpienie szablon jest po prostu klasa. Zasadniczo nie różni się od innych klas.

Kiedy masz typową definicję szablonu:

template<typename T> class Base; 

Następnie instancji szablonu:

Base<int> 

jest tylko klasa o tej nazwie. Nie ma to nic wspólnego z int i nie ma absolutnie żadnego związku, z int. Nie odziedziczy go w żaden sposób ani w żaden sposób.

Następny krok:

class Derived; 

template<typename T> class Base {}; 

Base<Derived> foo; 

Znowu Base<Derived> jest po prostu klasa. Nie ma żadnego wewnętrznego związku z Derived. Nie pochodzi z niego, nie dziedziczy po nim, nic.

Więc teraz bierzemy końcowy etap:

template<class T> 
class Base 
{ 

}; 

class Derived : public Base<Derived> 
{ 

}; 

Ten deklaruje klasę o nazwie Derived, która dziedziczy z klasy o nazwie Base<Derived>. A Base<Derived> to tylko klasa. To bardzo prosta klasa. Nie można zrobić nic prostszego. Nie ma żadnych metod. Nie ma żadnych członków. Ani prywatne, chronione ani publiczne. Jest to malutka klasa, ale ma te same prawa i przywileje, co każda inna klasa. Możesz zadeklarować do niego wskaźnik. Lub odniesienie do niego. To tylko klasa.

Znowu kluczową koncepcją jest to, że instancja szablonu jest po prostu klasą. Nie "dziedziczy" w żaden sposób z klasy, która jest parametrem szablonu. W tym przypadku instancja szablonu jest całkowicie pusta, ale może zrobić wszystko, co może zrobić każda inna klasa. Może mieć członków publicznych, prywatnych i chronionych. Może pochodzić z innej klasy, która może być inną instancją szablonu (ponieważ instancja szablonu jest tylko klasą). Inna klasa może pochodzić z instancji szablonu, ponieważ instancja szablonu jest po prostu klasą.

Nie ma tu nieskończonego gniazdowania. Po prostu masz jedną klasę dziedziczącą z innej klasy. Druga klasa to instancja szablonu, ale czy zdarzyło mi się wspomnieć, że instancja szablonu to tylko klasa?

3

Proste wytłumaczenie: bo po to całkowicie poprawny:

template<class T> 
class Base 
{ 
}; 

class Derived /* at this point, Derived is declared (not defined) */; 

int main() 
{ 
    Base<Derived> base; // the definition of Derived (or lack thereof) is irrelevant. 
} 
Powiązane problemy