我有一个可以用一组附加模板装饰的类,以提供其他功能。每个加载项都需要能够调用基类,而用户则需要能够调用基类(直接调用或使用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;
}

有人可以指出我可能做错了什么吗?

谢谢,
保罗·H

最佳答案

好吧,自从我开始使用Decorator方法以来,我还是很可能:)

编辑:让我们添加AddOnValues来解决this

这里的问题是多重继承。跟踪这种图并不容易,但是如果仔细观察,您会发现CMyClass<AddOn_A>从CBase继承了两次。

  • CMyClass<AddOn_A> AddOn_A<CBase> CBase
  • CMyClass<AddOn_A> CMyclass<empty,empty,empty> CBase

  • 问题在于您使用的是策略方法,而不是装饰器方法。在适当的Decorator方法中,层次结构严格是线性的,并且一次仅具有一个模板参数。让我们获得基础:
    // 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);
    

    当然,最后一种解决方案可以节省在 call 站点的键入内容,但是您必须在类里面真正地工作:)

    同样,您必须在继承的每个步骤中重复各种构造函数,而我很快就证明了这很无聊。

    那里有预处理器宏,但是...上次我花了大约500行来生成很简单的东西,所以不要打扰和打字,真的:)

    10-06 12:45