2016-06-04 13 views
3

Ich muss Vorlage als Funktionsargument übergeben, aber Vorlage muss alle geerbten Objekte akzeptieren.C++ - Vererbung im Template-Parameter

Zum Beispiel:

template <class T> 
class Template { 
public: 
    Template() {} 
}; 

// IMPORTANT: must accept all children of "Base" 
class Test { 
    Template<Base> *pointer; 
public: 
    Test(Template<Base> *pointer) {...} 
}; 


main() { 
    Template<Base> *base = new Template<Base>(); 
    Template<Child1> *child = new Template<Child1>(); 

    Test *test1 = new Test(base); // WORKING 
    // NEXT LINE IS IMPORTANT: 
    Test *test2 = new Test(child); // ERROR: no matching function for call to 'Test::Test(Template<Child1>*&)' 
} 


class Base {...}; 
class Child1 : public Base {...}; 
class Child2 : public Base {...}; 
.. 

Ich brauche die effektivste Art und Weise, wie Vorlage speichern in „Test“ Klasse, dass alle Kinder Klassen akzeptieren. Ich habe viele Kinderklassen.

1) Ist es möglich, einige Casting (static_cast) für Child-Klasse in Argument zu machen? Es wird gut sein, es innerhalb von "Vorlage" oder innerhalb der "Test" -Klasse zu verwenden. Nicht, wenn ein neues "Test" -Objekt erstellt wird, da es viele neue Objekte geben wird.

2) Oder übergeben Argument ohne Vorlage Argument Test(Template *pointer) {...}?

Oder wie man es löst?

+0

'Vorlage ' ist völlig unabhängig von 'Template '. Sie sind völlig verschiedene Klassen. Je nachdem, was Sie tun möchten, ist die Option "Löschen" möglicherweise eine sinnvolle Option. – Cornstalks

+0

Gibt es etwas, das Sie daran hindert, eine Basisklasse (nicht Vorlagenvorlage) zu Vorlage zu haben und diese stattdessen in Ihrer Testklasse zu speichern? – coyotte508

+0

@ coyotte508: Ja, akzeptable Lösung, aber ich würde gerne wissen, ob es eine direkte Lösung für die Vererbung von Template-Parametern gibt. – Nick

Antwort

2

Der Grund, dass die Sache, die Sie tun möchten, nicht funktioniert, ist, weil Schablonentypen Vererbung in typename Parametern nicht beachten.

Template<Base> *base 

und

Template<Child1> *child 

sind völlig verschiedene Typen. Der Versuch, einen anstelle des anderen zu verwenden, ist wie der Versuch, einen int an eine Funktion zu übergeben, die einen string Parameter hat.

Das sagte, ich glaube, Sie haben zwei realisierbare Optionen hier.

# 1 Make Test eine Vorlage zu

Eine Option wäre Test eine Vorlage zu machen:

template <typename T> 
class Test { 
    T *pointer; 
public: 
    Test(T *pointer) {...} 
}; 

Dies ermöglicht es dem Test Objekt mit zu arbeiten, was auch immer Objekt wurde an sie übergeben.

# 2 Verwenden Sie eine Schnittstelle

Alternativ kann durch eine Schnittstelle zu schaffen, und diese Schnittstelle in Template Implementierung, können Sie Ihre Schnittstelle zum Test Klasse übergeben:

/** 
* Interface that declares the virtual methods that 
* will be used by your Test class. 
*/ 
class ITemplate { 
public: 
    virtual ~ITemplate() {} 

    // Define your methods here. 
    virtual void Foo() = 0; 
    virtual void Bar() = 0; 
    virtual void Baz() = 0; 
} 

/** 
* Concrete ITemplate implementation. 
*/ 
template <class T> 
class Template : public ITemplate { 
public: 
    Template() {} 
    virtual ~Template() {} 

    virtual void Foo() override {} 
    virtual void Bar() override {} 
    virtual void Baz() override {} 

}; 

class Test { 
    ITemplate *pointer; 
public: 
    Test(ITemplate *pointer) {...} 
}; 

Dies hat den Vorteil, dass Sie kann Ihre Template Klasse ändern, ohne Ihre Test Klasse zu beeinträchtigen, weil die Test Klasse mit ITemplate Schnittstellen anstelle von konkreten Objekten arbeitet. Der Nachteil davon ist, dass Sie die Kosten für virtuelle Funktionsnachfragen auf sich nehmen, was unerwünscht ist, wenn Sie versuchen, extrem effizienten Code zu schreiben.

+0

Ich habe versucht, die erste Lösung vor, aber Sie müssen bestimmte Vorlage Typ 2 mal - für Vorlage und für Test-Klasse. Ich wollte eine bessere Lösung wissen. Wenn in Küstennähe keine Lösungen mehr angeboten werden, akzeptiere ich die Schnittstellenlösung. Vielen Dank! – Nick

+0

Ich habe ein Problem mit der Schnittstellenlösung. Wie verwende ich Methoden je nach Typ? Zum Beispiel habe ich in der "Template" -Klasse: 'void Foo (T) override {}', wie kann ich die Schnittstelle dafür machen? In ITemplate kann ich nicht haben: 'virtual void Foo (Base) = 0;'. Weder in der Template-Klasse: 'T Foo() überschreiben Sie {}' und in ITemplate: 'virtual Base Foo() = 0;' – Nick

+0

@Nick - Ich glaube nicht, dass Sie tun können, was Sie mit einer Schnittstelle machen wollen. 'T' wäre undefiniert, es sei denn, Sie hätten' Foo' als Template-Funktion verwendet, aber Sie können keine virtuelle Template-Funktion haben. –

1

1) Ist es möglich, einige Casting (static_cast) für die Child-Klasse in Argument zu machen? Es wird gut sein, es innerhalb von "Vorlage" oder innerhalb der "Test" -Klasse zu verwenden. Nicht, wenn ein neues "Test" -Objekt erstellt wird, da es viele neue Objekte geben wird.

Ich verstehe nicht genau, was Sie vorschlagen. Es ist nicht möglich, einen nicht verwandten Zeiger auf einen anderen zu übertragen.

2) Oder passieren Argument ohne Template-Argument

Nr Vorlagen nicht Funktionsargumente sein.

Was Sie tun können, ist zu anderen Instanzen von TemplateTemplate<T> implizit konvertierbar machen:

template <class T> 
class Template { 
public: 
    // ... 

    template<class U> 
    Template(const Template<U>&); 
}; 

Je nachdem, wie die Umwandlung Konstruktor implementiert ist, es Beschränkungen U verhängen kann.

Und weiter, wenn Sie eine Template<Base> Instanz innerhalb Test anstelle einem Zeiger speichern sind:

class Test { 
    Template<Base> member; 
    // ... 
}; 

Dann könnten Sie die Template<Base> von Wert übernehmen (kopieren) oder durch rvalue Verweis (zu bewegen) zum Konstruktor. Ein Template<Child> würde implizit Template<Base> umgewandelt werden:

class Test { 
    Template<Base> member; 
public: 
    Test(Template<Base> argument) {/* ... */} 
}; 

Dieser Ansatz ist das gleiche wie verwendet wird implizit up Gießen der Standard-Smart-Pointer-Typen zu ermöglichen.

+0

Danke, das ist eine interessante Lösung, nur ein Problem ist, dass es eine Kopie der Vorlage in Testobjekt erstellen wird. Wenn es eine ähnliche Lösung mit Zeiger (oder Bewegung) geben wird, wird es gut sein. – Nick

+0

@Nick sollten Sie überlegen, ob das Erstellen einer Kopie von 'Template ' eine schlechte Sache ist, und wenn ja, warum sollte es sein. Wenn Sie stattdessen verschieben möchten, wäre das sicher möglich, indem Sie 'Test (Template && Argument)' verwenden. Das Speichern eines Zeigers ist keine Option, da das implizit konvertierte 'Template ' ein temporäres ist und nach dem Aufruf des Konstruktors zerstört wird, so dass ein Zeiger auf den Zeiger hängen bleibt. – user2079303