декоратор с базой, которая требует аргумента конструктора - PullRequest
0 голосов
/ 21 октября 2009

У меня есть шаблон, похожий на декоратор, с базой, для которой требуется параметр конструктора. Декоратор построен таким образом, что он может принимать произвольное количество дополнительных компонентов в качестве параметров шаблона (до 3 в этом примере).

К сожалению, я не могу понять, как передать ему параметр конструктора базы, когда указано более одного дополнения. В приведенном ниже примере CMyClass< AddOn_A > A( 100 ); работает отлично, но CMyClass< AddOn_A, AddOn_B > AB( 100 ); генерирует ошибку в конструкторе CMyClass.

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;
}

Если кто-нибудь может указать, что я могу делать неправильно, пожалуйста, дайте мне знать.

Спасибо, PaulH

Ответы [ 2 ]

2 голосов
/ 22 октября 2009

Ваши ошибки обычно происходят из-за того, что CMyClass не имеет конструктора по умолчанию (потому что вместо этого вы определяете CMyClass(int)), поэтому необходимо явно создать экземпляр ваших родителей с помощью конструктора CMyClass(int), который вы иметь. Так, например, в вашем определении CMyClass вам нужно добавить вызов к CMyClass(int) в списке инициализатора

CMyClass(int x) : AddOn1<CBase>(x), CMyClass<AddOn2, AddOn3>(x) //send x down

Теперь, когда мы CMyClass отправляем x по линии, вашей специализации базового случая (CMyClass<empty, empty, empty>) необходимо иметь конструктор, который принимает x, но ничего не делает с ним

template<>
class CMyClass<empty, empty, empty> {
public:
    CMyClass(int) {} //do nothing
};

Теперь компилятор может найти нужные конструкторы и создать ваши классы, как вы ожидаете


Просто чтобы объяснить, почему строки типа CMyClass<AddOn_A> A(100) работают, это потому, что A (в этом примере) имеет только одного родителя, CMyClass<empty, empty, empty>, и вашу специализацию

template<> class CMyClass< empty, empty, empty > {};

имеет конструктор по умолчанию, потому что он пустой (или, более формально, потому что он не определяет других конструкторов). Это сразу ломается, когда вы вызываете CMyClass<AddOn_A, AddOn_B> AB(100), потому что у него есть два родителя, CMyClass<AddOn_B, empty, empty> и CMyClass<empty, empty, empty>, однако первый не имеет конструктора по умолчанию, поэтому компилятор не знает, как его построить , Вот почему мы должны добавить эту строку в список инициализаторов, поэтому мы сообщаем компилятору создать CMyClass<AddOn_B, empty, empty> с помощью его конструктора CMyClass(int x) (обратите внимание, что это означает, что компилятор также попытается создать CMyClass<empty, empty, empty> с параметром x , поэтому нам нужно добавить конструктор к той специализации, который будет принимать параметр).

0 голосов
/ 21 октября 2009
  • Не сразу понятно, чего вы пытаетесь достичь. Я надеюсь, что вы не путаете наследование и шаблоны.

  • empty, AddOn_A и AddOn_B являются шаблонами классов. Они не являются экземплярами класса. У вас должны быть настоящие классы.

...