I have a decorator-like pattern with a base that requires a constructor parameter. The decorator is constructed such that it can take an arbitrary number of add-on components as template parameters (up to 3 in this example).
Unfortunately, I can’t figure out how to pass the base’s constructor parameter to it when more than one add-on is specified. In the example below, CMyClass< AddOn_A > A( 100 ); works perfectly, but CMyClass< AddOn_A, AddOn_B > AB( 100 ); generates an error at the CMyClass constructor.
template< class Base >
class AddOn_A : public Base
{
public:
AddOn_A( int x ) : Base( x )
{
};
int AddOne()
{
return static_cast< Base* >( this )->DoSomething() + 1;
};
};
template< class Base >
class AddOn_B : public Base
{
public:
AddOn_B( int x ) : Base( x )
{
};
int AddTwo()
{
return static_cast< Base* >( this )->DoSomething() + 2;
};
};
class CBase
{
public:
explicit CBase( int x ) : x_( x )
{
};
int DoSomething()
{
return x_;
};
private:
int x_;
};
// define an empty AddOn
template< class > class 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 > {};
// 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:
// what needs to go here???
CMyClass( int x ) : AddOn1< CBase >( x )
{};
};
int _tmain( int argc, _TCHAR* argv[] )
{
// works
CMyClass< AddOn_A > A( 100 );
_ASSERT( A.AddOne() == 101 );
// works
CMyClass< AddOn_B > B( 100 );
_ASSERT( B.AddTwo() == 102 );
// generates an error at the CMyClass ctor:
// error C2512: 'CMyClass<AddOn1>' : no appropriate default constructor available
CMyClass< AddOn_A, AddOn_B > AB( 100 );
_ASSERT( AB.AddOne() == 101 );
_ASSERT( AB.AddTwo() == 102 );
return 0;
}
If anybody can point out what I may be doing wrong, please let me know.
Thanks,
PaulH
Your errors are generally originating out of the fact that
CMyClassdoes not have a default constructor (because you define aCMyClass(int)instead), so it is necessary to explicitly instantiate your parents with theCMyClass(int)constructor that you have. So, for example, in your definition ofCMyClassyou need to add the call toCMyClass(int)in the initializer listNow that we have
CMyClasssendingxdown the line, it is necessary for your base case specialization (CMyClass<empty, empty, empty>) to now have a constructor that acceptsxbut does nothing with itNow the compiler can find the right constructors and create your classes as you expect
Just to explain why lines like
CMyClass<AddOn_A> A(100)work, it’s becauseA(in that example) has only one parent,CMyClass<empty, empty, empty>, and your specializationdoes have a default constructor, because it’s empty (or, more formally, because it defines no other constructors). This breaks down immediately once you call
CMyClass<AddOn_A, AddOn_B> AB(100)because that has two parents,CMyClass<AddOn_B, empty, empty>andCMyClass<empty, empty, empty>, however the former does not have a default constructor, so the compiler does not know how to construct it. That’s why we must add that one line to the initializer list, so we tell the compiler to createCMyClass<AddOn_B, empty, empty>using itsCMyClass(int x)constructor (note how that means the compiler will also try to makeCMyClass<empty, empty, empty>with thexparameter, so we need to add a constructor to that specialization which will accept the parameter).