質問

追加の機能を提供する一連のアドオンテンプレートで装飾できるクラスがあります。各アドオンは基本クラスを呼び出すことができ、ユーザーは基本クラスを(直接またはCMyClassをプロキシとして使用して)呼び出すことができる必要があります。 残念ながら、コンパイラは呼び出している基本クラスを判別できず、あいまいなアクセスエラーが発生します。

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

誰かが私が間違っているかもしれないことを指摘できますか?

ありがとう、 PaulH

役に立ちましたか?

解決

まあ、デコレータアプローチを開始して以来、同様に:)

編集:AddOnValuesを追加して、これも解決します

ここでの問題は、多重継承です。このような図をトレースするのは簡単ではありませんが、よく見るとCMyClass<AddOn_A>がCBaseから2回継承していることがわかります。

  1. AddOn_A<CBase> <!> lt;-CBase <!> lt;-CMyclass<empty,empty,empty>
  2. <=> <!> lt;-<=> <!> lt;-<=>

問題は、デコレータアプローチではなくポリシーアプローチを使用したことです。適切なデコレータアプローチでは、階層は厳密に線形であり、一度に1つのテンプレートパラメータしかありません。基礎を取得しましょう:

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

これで実際に使用できるようになりました!

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

非常に簡単ですね。明らかな欠点は、そこに機能を追加しないことです...

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

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

さて...あまり美しくないでしょうね...さらに良いですか?さて、ラッパーを使用するだけです!

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

もちろん、最後の解決策は呼び出し側での入力を節約しますが、実際にクラスで作業する必要があります:)

また、継承の各ステップでさまざまなコンストラクターを繰り返す必要がありますが、これはすぐに証明されます...退屈です。

プリプロセッサマクロはありますが、...前回は非常に単純なものを生成するのに500行ほどかかりました。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top