Domanda

Ho una classe che può essere decorata con una serie di modelli aggiuntivi per fornire funzionalità aggiuntive.Ogni componente aggiuntivo deve essere in grado di chiamare la classe base e l'utente deve essere in grado di chiamare la classe base (direttamente o utilizzando CMyClass come proxy).Sfortunatamente, il compilatore non è in grado di stabilire quale classe base sto chiamando e ottengo errori di accesso ambigui.

template< class T >
class AddOn_A : public T
{
public: 
    AddOn_A( int x ) : T( x ) 
    {};

    int AddOne()
    {
        T* pT = static_cast< T* >( this );
        return pT->GetValue() + 1;
    };
};

template< class T >
class AddOn_B : public T
{
public: 
    AddOn_B( int x ) : T( x ) 
    {};

    int AddTwo()
    {
        T* pT = static_cast< T* >( this );
        return pT->GetValue() + 2;
    };
};

class CBase
{
public:
    explicit CBase( int x ) : x_( x ) 
    {
    };

    int GetValue()
    {
        return x_;
    };

private:
    int x_;
};

// define an empty AddOn
template< class > struct 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 > : public CBase
{
public:
    CMyClass( int x ) : CBase( x ) 
    {};
};

// 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:
    CMyClass( int x ) : AddOn1< CBase >( x ),
                        CMyClass< AddOn2, AddOn3 >( x )
    {};
};

int _tmain( int argc, _TCHAR* argv[] )
{
    CMyClass< AddOn_A > A( 100 );

    // error C2385: ambiguous access of 'GetValue'
    //     1>        could be the 'GetValue' in base 'CBase'
    //     1>        or could be the 'GetValue' in base 'CBase'
    _ASSERT( A.GetValue() == 100 );

    // error C2385: ambiguous access of 'GetValue'
    //     1>        could be the 'GetValue' in base 'CBase'
    //     1>        or could be the 'GetValue' in base 'CBase'
    _ASSERT( A.AddOne() == A.GetValue() + 1 );

    // works
    _ASSERT( A.AddOne() == 101 );

    CMyClass< AddOn_A, AddOn_B > AB( 100 );

    // same errors as above
    _ASSERT( AB.GetValue() == 100 );

    // same errors as above
    _ASSERT( AB.AddTwo() == AB.GetValue() + 2 );

    // works
    _ASSERT( AB.AddTwo() == 102 );

    return 0;
}

Qualcuno può indicarmi cosa potrei fare di sbagliato?

Grazie, Paulh

È stato utile?

Soluzione

Bene, dal momento che ho lanciato l'approccio Decorator, potrei anche :)

MODIFICARE:aggiungiamo gli AddOnValues ​​da risolvere Questo anche

Il problema qui è la multi-ereditarietà.Tracciare un diagramma del genere non è facile ma se guardi da vicino lo vedrai CMyClass<AddOn_A> eredita due volte da CBase.

  1. CMyClass<AddOn_A> <-- AddOn_A<CBase> <-- CBase
  2. CMyClass<AddOn_A> <-- CMyclass<empty,empty,empty> <-- CBase

Il problema è che hai utilizzato un approccio politico, invece di un approccio Decoratore.In un corretto approccio Decorator, la gerarchia è strettamente lineare e hai solo un parametro del modello alla volta.Prendiamo le basi:

// Note that the static_cast are completely unnecessary
// If you inherit from T then you can freely enjoy
// its public and protected methods
template< class T >
class AddOn_A : public T
{
public:
    enum { AddOnValues = T::AddOnValues | 0x01 }; // this hides T::AddOnValues

    AddOn_A( int x ) : T( x ) {};

    int AddOne()
    {
        return this->GetValue() + 1;
    };
};

template< class T >
class AddOn_B : public T
{
public:
    enum { AddOnValues = T::AddOnValues | 0x02 }; // this hides T::AddOnValues

    AddOn_B( int x ) : T( x ) {};

    int AddTwo()
    {
        return this->GetValue() + 2;
    };
};

class CBase
{
public:
    enum { AddOnValues = 0x00 };

    explicit CBase( int x ) : x_( x ) {}
    virtual ~CBase() {} // virtual destructor for inheritance

    int GetValue() const { return x_; }; // const method

private:
    int x_;
};

Ora possiamo passare all'utilizzo vero e proprio!

// First, the typedef approach
typedef AddOn_B< AddOn_A< CBase > > CMyClass;
CMyClass myObject(3);
std::cout << myObject.GetValue() << std::endl;
std::cout << myObject.AddOne() << std::endl;
std::cout << myObject.AddTwo() << std::endl;

Abbastanza facile, vero?L'ovvio svantaggio è che non aggiungi funzionalità lì ...

 // I want more!
 template < class T >
 class CMyClassImpl: public T
 {
   // Whatever you want
 };

 CMyClassImpl< AddOn_B< AddOn_A< CBase > > > myObject(3);

Va bene...non così bello, immagino...Anche meglio ?Bene, possiamo semplicemente usare un involucro!

 // Even better
 template <>
 class CMyClass: public CMyClassImpl < CBase > {};

 template < template <class> class AddOn1>
 class CMyClass: public CMyClassImpl <AddOn1 < CBase > > {};

 template < template <class> class AddOn1,
            template <class> class AddOn2 >
 class CMyClass: public CMyClassImpl < AddOn2 < AddOn1< CBase > > > {};

 template < template <class> class AddOn1,
            template <class> class AddOn2,
            template <class> class AddOn3 >
 class CMyClass: public CMyClassImpl < AddOn3 < AddOn2< AddOn1< CBase > > > > {};

 // Go on with as much specializations as you wish

 CMyClass < AddOn_A, AddOn_B > myObject(3);

Ovviamente, l'ultima soluzione evita di digitare sul sito chiamante, ma devi lavorare davvero sulla tua classe :)

Inoltre, devi ripetere i vari costruttori ad ogni passaggio dell'ereditarietà, cosa che dimostrerò rapidamente...noioso.

Esistono macro del preprocessore, ma...l'ultima volta mi ci sono volute circa 500 righe per generare qualcosa di abbastanza semplice, quindi non preoccuparti di digitare, davvero :)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top