Dziedziczenie z klasy szablonu w języku C ++

106

Powiedzmy, że mamy klasę szablonu Area, która ma zmienną składową T area, a T getArea()i void setArea(T)funkcje składowe.

Potrafię stworzyć Areaobiekt określonego typu pisząc Area<int>.

Teraz mam klasę, Rectanglektóra dziedziczy Areaklasę. Ponieważ Rectanglesam w sobie nie jest szablonem, nie mogę pisać Rectangle<int>.

Jak wyspecjalizować dziedziczony Areatyp Rectangleobiektów?

EDYCJA: Przepraszam, zapomniałem wyjaśnić - moje pytanie dotyczy tego, czy można dziedziczyć Area bez specjalizacji, więc nie jest ona dziedziczona jako Area of ​​ints, ale ponieważ Area Rectangle może specjalizować typy.

dtech
źródło
4
Jest to szablon klasy , ponieważ jest to szablon, z którego są generowane klasy.
sbi
1
@sbi Nie zamierzam tutaj rozpoczynać wojny o płomienie, ale jeśli Bjarne Stroustrup nie rozróżnia między szablonem klasy a klasą szablonu (zobacz Język programowania C ++ , wydanie 4, sekcja 23.2.1), to nie powinieneś też.
Michael Warner,
@MichaelWarner Wydaje mi się, że pamiętam, jak dokonywał rozróżnienia. To było jednak w latach 90. w Usenecie. Może od tego czasu się poddał. (A może odnosi się do utworzonego szablonu klasy jako klasy szablonu?)
sbi

Odpowiedzi:

245

Zrozumienie szablonów ma ogromne znaczenie dla zrozumienia terminologii, ponieważ sposób, w jaki o nich mówisz, determinuje sposób myślenia o nich.

W szczególności Areanie jest klasą szablonu, ale szablonem klasy. Oznacza to, że jest to szablon, z którego można generować klasy. Area<int>jest taką klasą (to nie jest obiekt, ale oczywiście możesz stworzyć obiekt z tej klasy w taki sam sposób, jak tworzysz obiekty z dowolnej innej klasy). Byłaby inna taka klasa Area<char>. Zauważ, że są to zupełnie różne klasy, które nie mają ze sobą nic wspólnego poza tym, że zostały wygenerowane z tego samego szablonu klas.

Ponieważ Areanie jest klasą, nie możesz wyprowadzić Rectanglez niej klasy . Możesz wyprowadzić klasę tylko z innej klasy (lub kilku z nich). Ponieważ Area<int>jest to klasa, możesz na przykład wyprowadzić Rectanglez niej:

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

Ponieważ Area<int>i Area<char>są różnymi klasami, możesz nawet wyprowadzać z obu jednocześnie (jednak podczas uzyskiwania dostępu do ich członków będziesz musiał poradzić sobie z niejasnościami):

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

Jednak podczas definiowania musisz określić, z których klas ma pochodzić Rectangle. Dzieje się tak bez względu na to, czy te klasy są generowane z szablonu, czy nie. Dwa obiekty tej samej klasy po prostu nie mogą mieć różnych hierarchii dziedziczenia.

Możesz także zrobić Rectangleszablon. Jeśli piszesz

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

Masz szablon, Rectanglez którego możesz pobrać klasę, z Rectangle<int>której pochodzi Area<int>, i inną klasę, z Rectangle<char>której pochodzi Area<char>.

Może się zdarzyć, że chcesz mieć jeden typ Rectangle, aby móc przekazywać różne rodzaje Rectangledo tej samej funkcji (która sama nie musi znać typu obszaru). Ponieważ Rectangle<T>klasy generowane przez tworzenie wystąpienia szablonu Rectanglesą formalnie niezależne od siebie, nie działa to w ten sposób. Możesz jednak skorzystać z wielokrotnego dziedziczenia tutaj:

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

Jeśli ważne jest, aby Twój rodzajowy Rectanglewywodził się z generycznego Area, możesz zrobić tę samą sztuczkę z 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
źródło
Uwaga: Parametr typu szablonu jest częścią klasy obiektu, który tworzy wystąpienie tego szablonu klasy. Innymi słowy, nie jest to składnik klasy, jest częścią typu.
Nikos
21

Czy po prostu próbujesz wyprowadzić Area<int>? W takim przypadku robisz to:

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

EDYCJA: Po wyjaśnieniu wygląda na to, że faktycznie próbujesz również utworzyć Rectangleszablon, w takim przypadku następujące czynności powinny działać:

template <typename T>
class Rectangle : public Area<T>
{
    // ...
};
Stuart Golodetz
źródło
8
class Rectangle : public Area<int> {
};
ldanko
źródło
8

Uczyń Rectangle szablonem i przekaż nazwę typu do Area:

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

};
pezcode
źródło
6

Rectanglebędzie musiał być szablonem, w przeciwnym razie jest to tylko jeden typ . Nie może być nie szablonem, podczas gdy jego podstawa jest magicznie. (Jego podstawą może być wystąpienie szablonu , chociaż wydaje się, że chcesz zachować funkcjonalność bazy jako szablonu ).

Wyścigi lekkości na orbicie
źródło
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
źródło
co by było, gdybyś miał metody w klasie pochodnej? Jak byś je zdefiniował? A większość jeszcze importuje, przynajmniej dla mnie, jak wywołać / użyć tych metod w funkcji main ()?
Pototo
6
Po pierwsze, jest to naprawdę stare pytanie, na które już jest świetna odpowiedź. Po drugie, unikaj odpowiedzi (i pytań), które są tylko kodem. Warto również dołączyć szczegółowe wyjaśnienia.
marcman