Frage

Ich habe eine Klasse, die mit einer Reihe von Add-on-Vorlagen dekoriert werden kann, um zusätzliche Funktionalität bereitzustellen.Jedes Add-on muss in der Lage sein, die Basisklasse aufzurufen, und der Benutzer muss in der Lage sein, die Basisklasse aufzurufen (entweder direkt oder unter Verwendung der CMyClass als Proxy).Leider kann der Compiler nicht erkennen, welche Basisklasse ich aufrufe, und ich erhalte mehrdeutige Zugriffsfehler.

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

Kann jemand darauf hinweisen, was ich möglicherweise falsch mache?

Danke, Paulh

War es hilfreich?

Lösung

Nun, da ich mit dem Decorator-Ansatz begonnen habe, könnte ich das auch tun :)

BEARBEITEN:Fügen wir die zu lösenden AddOnValues ​​hinzu Das sowie

Das Problem hierbei ist die Mehrfachvererbung.Ein solches Diagramm nachzuzeichnen ist nicht einfach, aber wenn Sie genau hinschauen, werden Sie es erkennen CMyClass<AddOn_A> erbt zweimal von CBase.

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

Das Problem besteht darin, dass Sie einen Richtlinienansatz anstelle eines Decorator-Ansatzes verwendet haben.Bei einem richtigen Decorator-Ansatz ist die Hierarchie streng linear und Sie haben jeweils nur einen Vorlagenparameter.Holen wir uns die Basis:

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

Jetzt können wir zur eigentlichen Verwendung kommen!

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

Ganz einfach, nicht wahr?Der offensichtliche Nachteil besteht darin, dass Sie dort keine Funktionalität hinzufügen ...

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

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

Okay...nicht so schön, denke ich...Noch besser ?Nun, wir können einfach einen Wrapper verwenden!

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

Natürlich erspart die letzte Lösung das Tippen auf der aufrufenden Seite, aber Sie müssen wirklich an Ihrer Klasse arbeiten :)

Außerdem müssen Sie die verschiedenen Konstruktoren bei jedem Schritt der Vererbung wiederholen, was ich schnell beweisen kann ...langweilig.

Es gibt Präprozessormakros, aber ...Letztes Mal habe ich etwa 500 Zeilen gebraucht, um etwas ganz Einfaches zu generieren, also machen Sie sich nicht die Mühe und tippen Sie, wirklich :)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top