Depende del uso que haga la plantilla con el tipo pasado. Si se refiere a contenedores estándar (por ejemplo, std::vector
, std::map
, etc.), la respuesta es no. No existe ninguna relación entre std::vector<Animal>
y std::vector<Dog>
, incluso si en la jerarquía de su clase los perros provienen de animales.
No se puede poner un Dog
en un std::vector<Animal>
... C++ usos copiar semántico y se incurriría en lo que se llama "slicing", que significa que su Dog
instancia perderá cualquier miembro que no está presente también en la clase base Animal
.
Sin embargo, en general, es muy posible que una plantilla use el tipo de diferentes maneras que permitan aceptar instancias de clases derivadas. Por ejemplo, en el siguiente código, la plantilla MethodCaller
se puede crear una instancia con un tipo pero usando una instancia de un tipo derivado y manejando correctamente el envío de enlace tardío. Esto es posible porque la instancia MethodCaller
solo contiene una referencia y no hace una copia del objeto.
#include <stdio.h>
template<typename T>
struct MethodCaller
{
T& t;
void (T::*method)();
MethodCaller(T& t, void (T::*method)())
: t(t), method(method)
{}
void operator()() { (t.*method)(); }
};
struct Animal { virtual void talk() = 0; };
struct Dog : Animal { virtual void talk() { printf("Bark\n"); } };
struct Cat : Animal { virtual void talk() { printf("Meow\n"); } };
struct Crocodile : Animal { virtual void talk() { printf("??\n"); } };
void makenoise(Animal *a)
{
MethodCaller<Animal> noise(*a, &Animal::talk);
noise(); noise(); noise();
}
int main()
{
Dog doggie;
Cat kitten;
Crocodile cocco;
makenoise(&doggie);
makenoise(&kitten);
makenoise(&cocco);
}
También es posible implementar la clase Stack
como desee ...
#include <vector>
template<typename T>
struct Stack
{
std::vector<T *> content;
~Stack()
{
for (int i=0,n=content.size(); i<n; i++)
delete content[i];
}
template<class S>
void push(const S& s)
{
content.push_back(new S(s));
}
template<class S>
S pop()
{
S result(dynamic_cast<S&>(*content.back()));
content.pop_back();
return result;
}
private:
// Taboo
Stack(const Stack&);
Stack& operator=(const Stack&);
};
int main()
{
Dog doggie;
Cat kitten;
Crocodile cocco;
Stack<Animal> s;
s.push(doggie);
s.push(kitten);
s.push(cocco);
Crocodile cocco2 = s.pop<Crocodile>();
Cat kitten2 = s.pop<Cat>();
Dog doggie2 = s.pop<Dog>();
}
Tenga en cuenta que en la aplicación que he usado un std::vector
para mantener punteros a los animales y por lo tanto para evitar el problema de corte. He estado usando un método de plantilla para poder aceptar tipos derivados en la llamada de inserción.
También tenga en cuenta que cuando saltan los animales debe proporcionar lo que es la clase y si es una persona equivocada (por ejemplo, que el pop a cabo una Crocodile
cuando el elemento superior de la pila es una Dog
) obtendrá una excepción bad_cast
en tiempo de ejecución.
Para contenedores de punteros, solo puedo recomendar el uso de Boost Pointer Containers como base. Es como punteros inteligentes, pero para objetos múltiples y con la mezcla habitual de propiedades que esperas de STL. –