7

Angenommen, ich habe eine Basisklasse, die die Klonierung von abgeleiteten Klassen:Derived eigentümlich wiederkehrende Vorlagen und Kovarianz

class Base 
{ 
    public: 
     virtual Base * clone() 
     { 
      return new Base(); 
     } 

     // ... 
}; 

Ich habe eine Reihe von abgeleiteten Klassen, die ein eigentümlich wiederkehrend Schablonenmuster implementiert sind:

template <class T> 
class CRTP : public Base 
{ 
    public: 
     virtual T * clone() 
     { 
      return new T(); 
     } 

     // ... 
}; 

Und ich versuche, von dem weiter wie folgt abzuleiten:

class Derived : public CRTP<Derived> 
{ 
    public: 
     // ... 
}; 

erhalte ich Kompilierungsfehlern auf die Wirkung von:

error C2555: 'CRTP<T>::clone': overriding virtual function return type differs and is not covariant from 'Base::clone' 

Ich weiß, dies wahrscheinlich nicht vollständig ein Ergebnis des Compilers ist den Vererbungsbaum für Abgeleitet wenn Instanziieren CRTP zu kennen. Außerdem wird auch der Rückgabetyp (T *) durch (Base *) ersetzt. Ich würde jedoch gerne wissen, ob es eine Arbeit gibt, die die obige Semantik beibehält.

+1

Für was es wert ist, geben GCC 4.1.2 und 4.7.1 beide ähnliche Fehler. –

+1

Duplizieren von http://stackoverflow.com/questions/15570333/crtp-and-dynamic-polymorphism-compile-error – erikced

+0

@erikced Danke für die Köpfe hoch. Es scheint, als ob ich nicht viel tun kann, ich werde nur den Rückgabetyp ersetzen. – Whanhee

Antwort

3

Eine nicht so schöne Problemumgehung.

class Base 
{ 
    protected: 
     virtual Base * clone_p() 
     { 
      return new Base(); 
     } 
}; 


template <class T> 
class CRTP : public Base 
{ 
    protected: 
     virtual CRTP* clone_p() 
     { 
      return new T; 
     } 
    public: 
     T* clone() 
     { 
      CRTP* res = clone_p(); 
      return static_cast<T*>(res); 
     } 
}; 


class Derived : public CRTP<Derived> 
{ 
    public: 
}; 

Verwenden dynamic_cast<> statt static wenn Sie glauben, es sicherer ist.

+0

Danke für die Hilfe. Nicht ganz die Semantik, nach der ich suche, denn idealerweise hätte Base den gleichen clone() Call. – Whanhee

1

Wenn Sie eine andere Syntax mit, die für die Angabe vollständige Typen verwenden leben können, können Sie die folgende (Warnung: ungetesteten Code): do

Lassen Sie uns zunächst Start mit der Maschinen:

// this gives the complete type which needs to be used to create objects 
// and provides the implementation of clone() 
template<typename T> class Cloneable: 
    public T 
{ 
public: 
    template<typename... U> Cloneable(U&&... u): T(std::forward<U>(u) ...) {} 
    T* clone() { return new Cloneable(*this); } 
private: 
    // this makes the class complete 
    // Note: T:: to make it type dependent, so it can be found despite not yet defined 
    typename T::CloneableBase::CloneableKey unlock() {} 
}; 

// this provides the clone function prototype and also makes sure that only 
// Cloneable<T> can be instantiated 
class CloneableBase 
{ 
    template<typename T> friend class Cloneable; 

    // this type is only accessible to Clonerable instances 
    struct CloneableKey {}; 

    // this has to be implemented to complete the class; only Cloneable instances can do that 
    virtual CloneableKey unlock() = 0; 
public: 
    virtual CloneableBase* clone() = 0; 
    virtual ~CloneableBase() {} 
}; 

OK, jetzt die eigentliche Klassenhierarchie. Das ist ziemlich Standard; keine CRTP-Zwischenprodukte oder andere Komplikationen. Allerdings implementiert keine Klasse die clone-Funktion, aber alle erben die Deklaration (direkt oder indirekt) von CloneableBase.

// Base inherits clone() from CloneableBase 
class Base: 
    public CloneableBase 
{ 
    // ... 
}; 

// Derived can inherit normally from Base, nothing special here 
class Derived: 
    public Base 
{ 
    // ... 
}; 

Hier ist, wie Sie dann Objekte erstellen:

// However, to create new instances, we actually need to use Cloneable<Derived> 
Cloneable<Derived> someObject; 
Derived* ptr = new Cloneable<Derived>(whatever); 

// Now we clone the objects 
Derived* clone1 = someObject.clone(); 
Derived* clone2 = ptr->clone(); 

// we can get rid og the objects the usual way: 
delete ptr; 
delete clone1; 
delete clone2; 

Beachten Sie, dass ein Cloneable<Derived> ist-ein Derived (es ist eine Unterklasse), deshalb müssen Sie Cloneable für den Bau nur verwenden, und kann nichts anderes vorgeben mit Derived Objekte zu arbeiten (gut, tyepinfo wird es auch als Cloneable<Derived> identifizieren).