неоднозначный доступ при вызове оформленного базового класса

StackOverflow https://stackoverflow.com/questions/1608733

  •  05-07-2019
  •  | 
  •  

Вопрос

У меня есть класс, который может быть украшен набором дополнительных шаблонов для обеспечения дополнительной функциональности.Каждое дополнение должно иметь возможность вызывать базовый класс, и пользователь должен иметь возможность вызывать базовый класс (либо напрямую, либо используя 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