7

Supposons que j'ai une classe de base qui clonage des classes dérivées:dérivés curieusement modèles récurrents et covariance

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

     // ... 
}; 

j'ai un ensemble de classes dérivées qui sont mis en oeuvre en utilisant un motif de modèle curieusement récurrent:

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

     // ... 
}; 

Et je tente de tirer de cette plus comme ceci:

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

Je reçois des erreurs de compilation à l'effet de:

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

Je sais que cela est probablement le résultat du compilateur de ne pas connaître entièrement l'arbre d'héritage pour les dérivés lors de l'instanciation CRTP. De plus, le remplacement du type de retour (T *) par (Base *) est également compilé. Cependant, j'aimerais savoir s'il existe un travail qui conserve la sémantique ci-dessus.

+1

Pour ce que ça vaut, GCC 4.1.2 et 4.7.1 à la fois donner des erreurs similaires. –

+1

en double de http://stackoverflow.com/questions/15570333/crtp-and-dynamic-polymorphism-compile-error – erikced

+0

@erikced Merci pour le heads up. Il semble qu'il n'y a pas grand-chose que je puisse faire, je vais juste remplacer le type de retour. – Whanhee

Répondre

3

Une solution de contournement pas si jolie.

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

Utilisation dynamic_cast<> au lieu de static si vous sentez qu'il est plus sûr.

+0

Merci pour l'aide. Pas tout à fait la sémantique que je recherche, puisque idéalement la base aurait le même appel clone(). – Whanhee

1

Si vous pouvez vivre avec avoir à utiliser une syntaxe différente pour spécifier les types complets, vous pouvez effectuer les opérations suivantes (avertissement: code non testé):

Let premier démarrage de la machine:

// 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, maintenant la hiérarchie de classe réelle. Celui-là est assez standard; pas d'intermédiaires CRTP ou d'autres complications. Cependant, aucune classe n'implémente la fonction clone, mais toutes héritent de la déclaration (directement ou indirectement) de CloneableBase.

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

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

Voici comment puis créer des objets:

// 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; 

Notez qu'un Cloneable<Derived> est-un Derived (il est une sous-classe), donc vous devez utiliser Cloneable uniquement pour la construction, et peut par ailleurs prétendre pour travailler avec Derived objets (bien, tyepinfo l'identifiera également comme Cloneable<Derived>).