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