سؤال

لدي فصل يمكن تزيينه بمجموعة من القوالب الإضافية لتوفير وظائف إضافية. يجب أن تكون كل وظيفة إضافية قادرة على الاتصال بالفئة الأساسية ويجب أن يكون المستخدم قادرًا على استدعاء الفئة الأساسية (إما مباشرة أو باستخدام 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;
}

هل يمكن لأي شخص أن يشير إلى ما قد أفعله خطأ؟

شكرا يا بول

هل كانت مفيدة؟

المحلول

حسنًا ، منذ أن أطلقت على نهج الديكور ، قد أكون كذلك :)

تحرير: دعنا نضيف القيمة الإضافية لحلها هذه كذلك

المشكلة هنا هي التعددية. إن تتبع مثل هذا الرسم التخطيطي ليس بالأمر السهل ولكن إذا نظرت عن كثب ، فسترى ذلك CMyClass<AddOn_A> يرث مرتين من CBASE.

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

المشكلة هي أنك استخدمت نهج السياسة ، بدلاً من نهج الديكور. في نهج الديكور المناسب ، يكون التسلسل الهرمي خطيرًا تمامًا ولديك فقط معلمة قالب واحد في وقت واحد. دعنا نحصل على الأساس:

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