Heredar de una clase de plantilla en c ++

106

Digamos que tenemos una clase de plantilla Area, que tiene una variable miembro T area, una función miembro T getArea()y una void setArea(T).

Puedo crear un Areaobjeto de un tipo específico escribiendo Area<int>.

Ahora tengo una clase Rectangleque hereda la Areaclase. Dado que en Rectanglesí mismo no es una plantilla, no puedo escribir Rectangle<int>.

¿Cómo me especializo en el Areatipo heredado de Rectangleobjetos?

EDITAR: Lo siento, olvidé aclarar: mi pregunta es si es posible heredar el Área sin especializarla, por lo que no se hereda como Área de ints sino como Área Rectángulo para la que se pueden especializar los tipos.

dtech
fuente
3
Es una plantilla de clase , porque es una plantilla a partir de la cual se generan las clases.
sbi
1
@sbi No tengo la intención de iniciar una guerra de llamas aquí, pero si Bjarne Stroustrup no establece una distinción entre plantilla de clase y clase de plantilla (consulte El lenguaje de programación C ++ , 4a ed., sección 23.2.1), entonces no debería tampoco.
Michael Warner
@MichaelWarner Creo recordar que hizo la distinción. Eso fue en los 90, sin embargo, en Usenet. Quizás se ha rendido desde entonces. (¿O tal vez se refiere a una plantilla de clase instanciada como una clase de plantilla?)
sbi

Respuestas:

244

Para entender las plantillas, es una gran ventaja tener la terminología correcta porque la forma en que se habla de ellas determina la forma de pensar en ellas.

Específicamente, Areano es una clase de plantilla, sino una plantilla de clase. Es decir, es una plantilla a partir de la cual se pueden generar clases. Area<int>es una clase de este tipo (que es no un objeto, pero por supuesto se puede crear un objeto de esa clase de la misma manera que se pueden crear objetos de cualquier otra clase). Otra de esas clases sería Area<char>. Tenga en cuenta que esas son clases completamente diferentes, que no tienen nada en común excepto por el hecho de que se generaron a partir de la misma plantilla de clase.

Dado Areaque no es una clase, no puede derivar la clase Rectanglede ella. Solo puede derivar una clase de otra clase (o varias de ellas). Dado que Area<int>es una clase, podría, por ejemplo, derivar Rectanglede ella:

class Rectangle:
  public Area<int>
{
  // ...
};

Dado que Area<int>y Area<char>son clases diferentes, incluso puede derivar de ambas al mismo tiempo (sin embargo, al acceder a miembros de ellas, tendrá que lidiar con ambigüedades):

class Rectangle:
  public Area<int>,
  public Area<char>
{
  // ...
};

Sin embargo, debe especificar de qué clase derivar cuando defina Rectangle. Esto es cierto sin importar si esas clases se generan a partir de una plantilla o no. Dos objetos de la misma clase simplemente no pueden tener diferentes jerarquías de herencia.

Lo que puede hacer es crear Rectangleuna plantilla también. Si tú escribes

template<typename T> class Rectangle:
  public Area<T>
{
  // ...
};

Tiene una plantilla Rectanglede la que puede obtener una clase de la Rectangle<int>que se deriva Area<int>y una clase diferente de la Rectangle<char>que se deriva Area<char>.

Puede ser que desee tener un solo tipo Rectanglepara poder pasar todo tipo de Rectanglea la misma función (que en sí misma no necesita conocer el tipo de Área). Dado que las Rectangle<T>clases generadas al crear instancias de la plantilla Rectangleson formalmente independientes entre sí, no funciona de esa manera. Sin embargo, puede hacer uso de la herencia múltiple aquí:

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);
}

Si es importante que su genérico Rectanglese derive de un genérico, Areatambién puede hacer el mismo truco con Area:

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
};
celtschk
fuente
Nota: Un parámetro de tipo de plantilla es parte de la clase de un objeto que crea una instancia de esa plantilla de clase. En otras palabras, no es un componente de la clase, es parte del tipo.
Nikos
21

¿Estás tratando de derivar Area<int>? En cuyo caso haces esto:

class Rectangle : public Area<int>
{
    // ...
};

EDITAR: Después de la aclaración, parece que en realidad también está tratando de hacer Rectangleuna plantilla, en cuyo caso lo siguiente debería funcionar:

template <typename T>
class Rectangle : public Area<T>
{
    // ...
};
Stuart Golodetz
fuente
8
class Rectangle : public Area<int> {
};
ldanko
fuente
8

Convierta Rectangle en una plantilla y pase el nombre del tipo a Area:

template <typename T>
class Rectangle : public Area<T>
{

};
pezcode
fuente
6

Rectangletendrá que ser una plantilla, de lo contrario, es solo un tipo . No puede ser una no plantilla mientras su base lo sea mágicamente. (Su base puede ser una instanciación de plantilla , aunque parece que desea mantener la funcionalidad de la base como plantilla ).

Carreras de ligereza en órbita
fuente
3
#include<iostream>

using namespace std;

template<class t> 
class base {
protected:
    t a;
public:
    base(t aa){
        a = aa;
        cout<<"base "<<a<<endl;
    }
};

template <class t> 
class derived: public base<t>{
    public:
        derived(t a): base<t>(a) {
        }
        //Here is the method in derived class 
    void sampleMethod() {
        cout<<"In sample Method"<<endl;
    }
};

int main() {
    derived<int> q(1);
    // calling the methods
    q.sampleMethod();
}
Narendra kumawat
fuente
¿y si tuvieras métodos en la clase derivada? ¿Cómo los definirías? Y la mayoría importa todavía, al menos para mí, ¿cómo llamas / usas esos métodos en tu función main ()?
Pototo
6
Primero, esta es una pregunta muy antigua que ya tiene una gran respuesta. En segundo lugar, evite las respuestas (y preguntas) que son solo códigos. También es útil incluir explicaciones detalladas.
marcman