décorateur avec une base qui nécessite un argument constructeur
Question
J'ai un motif de type décorateur avec une base qui nécessite un paramètre constructeur. Le décorateur est construit de telle sorte qu'il puisse prendre un nombre arbitraire de composants additionnels comme paramètres de modèle (jusqu'à 3 dans cet exemple).
Malheureusement, je ne vois pas comment lui transmettre le paramètre constructeur de la base lorsque plusieurs add-ons sont spécifiés. Dans l'exemple ci-dessous, CMyClass< AddOn_A > A( 100 );
fonctionne parfaitement, mais CMyClass< AddOn_A, AddOn_B > AB( 100 );
génère une erreur au niveau du constructeur CMyClass.
template< class Base >
class AddOn_A : public Base
{
public:
AddOn_A( int x ) : Base( x )
{
};
int AddOne()
{
return static_cast< Base* >( this )->DoSomething() + 1;
};
};
template< class Base >
class AddOn_B : public Base
{
public:
AddOn_B( int x ) : Base( x )
{
};
int AddTwo()
{
return static_cast< Base* >( this )->DoSomething() + 2;
};
};
class CBase
{
public:
explicit CBase( int x ) : x_( x )
{
};
int DoSomething()
{
return x_;
};
private:
int x_;
};
// define an empty AddOn
template< class > class empty {};
// forward declaration and Add-On defaults
template< template< class > class AddOn1 = empty,
template< class > class AddOn2 = empty,
template< class > class AddOn3 = empty >
class CMyClass;
// specialized template for the default case
template<> class CMyClass< empty, empty, empty > {};
// actual definition
template< template< class > class AddOn1,
template< class > class AddOn2,
template< class > class AddOn3 >
class CMyClass : public AddOn1< CBase >,
public CMyClass< AddOn2, AddOn3 >
{
public:
// what needs to go here???
CMyClass( int x ) : AddOn1< CBase >( x )
{};
};
int _tmain( int argc, _TCHAR* argv[] )
{
// works
CMyClass< AddOn_A > A( 100 );
_ASSERT( A.AddOne() == 101 );
// works
CMyClass< AddOn_B > B( 100 );
_ASSERT( B.AddTwo() == 102 );
// generates an error at the CMyClass ctor:
// error C2512: 'CMyClass<AddOn1>' : no appropriate default constructor available
CMyClass< AddOn_A, AddOn_B > AB( 100 );
_ASSERT( AB.AddOne() == 101 );
_ASSERT( AB.AddTwo() == 102 );
return 0;
}
Si quelqu'un peut dire ce que je peux faire de mal, merci de me le faire savoir.
Merci, PaulH
La solution
Vos erreurs proviennent généralement du fait que CMyClass
n'a pas de constructeur par défaut (car vous définissez plutôt un CMyClass(int)
), il est donc nécessaire d'instancier explicitement vos parents avec le constructeur x
que vous avoir. Ainsi, par exemple, dans votre définition de CMyClass<empty, empty, empty>
, vous devez ajouter l'appel à CMyClass<AddOn_A> A(100)
dans la liste d'initialisation
CMyClass(int x) : AddOn1<CBase>(x), CMyClass<AddOn2, AddOn3>(x) //send x down
Maintenant que nous avons A
envoyé CMyClass<AddOn_A, AddOn_B> AB(100)
ultérieurement, il est nécessaire que votre spécialisation de cas de base (CMyClass<AddOn_B, empty, empty>
) ait maintenant un constructeur qui accepte CMyClass(int x)
mais ne fait rien avec elle
template<>
class CMyClass<empty, empty, empty> {
public:
CMyClass(int) {} //do nothing
};
Le compilateur peut maintenant trouver les constructeurs appropriés et créer vos classes comme vous le souhaitez
Juste pour expliquer pourquoi des lignes comme <=> fonctionnent, c'est parce que <=> (dans cet exemple) n'a qu'un seul parent, <=> et votre spécialisation
.template<> class CMyClass< empty, empty, empty > {};
a-t-il un constructeur par défaut, car il est vide (ou, plus formellement, car il ne définit aucun autre constructeur). Cela tombe en panne immédiatement une fois que vous appelez <=> parce que cela a deux parents, <=> et <=>. Cependant, le premier n'a pas de constructeur par défaut, le compilateur ne sait donc pas comment construire il. C’est pourquoi nous devons ajouter cette ligne à la liste d’initialisation. Nous demandons donc au compilateur de créer <=> à l’aide de son constructeur <=> (remarque: cela signifie que le compilateur essaiera également de créer <=> avec le paramètre <=> , nous devons donc ajouter un constructeur à cette spécialisation qui acceptera le paramètre).
Autres conseils
-
Ce que vous essayez d’atteindre n’est pas immédiatement clair. J'espère que vous ne confondez pas héritage et modèles.
-
empty
,AddOn_A
etAddOn_B
sont des modèles de classe. Ce ne sont pas des instances d'une classe. Vous devez avoir des classes réelles.