Per comprendere i modelli, è di enorme vantaggio ottenere la terminologia diretta perché il modo in cui ne parli determina il modo in cui pensarli.
In particolare, Area
non è una classe modello, ma un modello di classe. Cioè, è un modello da cui è possibile generare classi. Area<int>
è una tale classe ( non è un oggetto, ma ovviamente puoi creare un oggetto da quella classe nello stesso modo in cui puoi creare oggetti da qualsiasi altra classe). Un'altra classe simile sarebbe Area<char>
. Si noti che quelle sono classi completamente diverse, che non hanno nulla in comune tranne il fatto che sono state generate dallo stesso modello di classe.
Poiché Area
non è una classe, non puoi derivarne la classe Rectangle
. Puoi solo derivare una classe da un'altra classe (o più di esse). Poiché Area<int>
è una classe, potresti, ad esempio, derivarne Rectangle
:
class Rectangle:
public Area<int>
{
// ...
};
Poiché Area<int>
e Area<char>
sono classi diverse, puoi anche derivare da entrambe contemporaneamente (tuttavia quando accedi ai membri di esse, dovrai affrontare le ambiguità):
class Rectangle:
public Area<int>,
public Area<char>
{
// ...
};
Tuttavia è necessario specificare da quale classe derivare quando si definisce Rectangle
. Questo è vero indipendentemente dal fatto che quelle classi siano generate da un modello o meno. Due oggetti della stessa classe semplicemente non possono avere gerarchie di ereditarietà diverse.
Quello che puoi fare è creare anche Rectangle
un modello. Se scrivi
template<typename T> class Rectangle:
public Area<T>
{
// ...
};
Hai un modello Rectangle
da cui puoi ottenere una classe da Rectangle<int>
cui deriva Area<int>
e una classe diversa da Rectangle<char>
cui deriva Area<char>
.
È possibile che tu voglia avere un solo tipo in Rectangle
modo da poter passare tutti i tipi di Rectangle
alla stessa funzione (che di per sé non ha bisogno di conoscere il tipo di Area). Poiché le Rectangle<T>
classi generate dall'istanza del modello Rectangle
sono formalmente indipendenti l'una dall'altra, non funziona in questo modo. Tuttavia puoi utilizzare l'ereditarietà multipla qui:
class Rectangle // not inheriting from any Area type
{
// Area independent interface
};
template<typename T> class SpecificRectangle:
public Rectangle,
public Area<T>
{
// Area dependent stuff
};
void foo(Rectangle&); // A function which works with generic rectangles
int main()
{
SpecificRectangle<int> intrect;
foo(intrect);
SpecificRectangle<char> charrect;
foo(charrect);
}
Se è importante che il tuo generico Rectangle
derivi da un generico Area
, puoi fare lo stesso trucco Area
anche con :
class Area
{
// generic Area interface
};
class Rectangle:
public virtual Area // virtual because of "diamond inheritance"
{
// generic rectangle interface
};
template<typename T> class SpecificArea:
public virtual Area
{
// specific implementation of Area for type T
};
template<typename T> class SpecificRectangle:
public Rectangle, // maybe this should be virtual as well, in case the hierarchy is extended later
public SpecificArea<T> // no virtual inheritance needed here
{
// specific implementation of Rectangle for type T
};