Конструктор пересылки с CRTP - PullRequest
0 голосов
/ 01 марта 2012

Я использую класс шаблона с CRTP для реализации шаблона клона, со вторым параметром шаблона Base, чтобы обеспечить несколько уровней наследования. Я получаю ошибку компилятора при попытке вызвать конструктор косвенного базового класса.

class B
{
public:
    B() {} //trivial constructor
    virtual B* clone()=0;
};

template<class Base, class Derived>
class Clonable
    :public Base //weird, I know
{
public:
    virtual B* clone() {return new Derived(*this);}
};

class D1 : public Clonable<B, D1>
{
public:
    D1(int a); //non-trivial constructor. Different signature than B
};

class D2 : public Clonable<D1, D2>
{
public:
    D2(int a): D1(a) {} //compiler error here
}

Единственное решение, с которым я сталкивался до сих пор, - это использование конструктора шаблонов с переменными числами в Cloneable, но мой компилятор (VC ++ 11) еще не реализовал их.

1 Ответ

6 голосов
/ 01 марта 2012

Вам нужно разрешить своим клонирующим аргументам прямого конструктора класса "middleman" или, что лучше (Люк Дантон предложил это), использовать наследование конструктора C ++ 11.

Итак, это легко сделать в C ++ 11, но это не так просто в C ++ 03 или с текущим компилятором, который еще не поддерживает пересылку аргументов C ++ 11 или наследование конструктора, например Visual C ++ 10.

Один из способов сделать это в C ++ 03 с использованием класса перенаправителя вспомогательных аргументов обсуждается в моем старом блоге "3 способа смешивания в общей реализации клонирования" . Тогда класс middleman (реализация клонирования) может выглядеть так:

template< class Derived, class Base >
class WithCloningOf
    : public progrock::cppx::ConstructorArgForwarder< Base >
{
protected:
    virtual WithCloningOf* virtualClone() const
    {
        return new Derived( *static_cast< Derived const* >( this ) );
    }

public:
    template< class ArgPack >
    WithCloningOf( ArgPack const& args )
        : progrock::cppx::ConstructorArgForwarder< Base >( args )
    {}

    std::auto_ptr< Derived > clone() const
    {
        return std::auto_ptr< Derived >(
            static_cast< Derived* >( virtualClone() )
            );
    }
};

Я обсуждал совместимость с C ++ 03 ConstructorArgForwarder в предыдущих публикациях в блоге; это может выглядеть так:

template< typename Type >
class ConstructorArgForwarder
    : public Type
{
public:
    typedef Type        Base;

    // TODO: remove
    virtual ~ConstructorArgForwarder() {}

    ConstructorArgForwarder( EmptyArgPack const& )
        : Base()
    {}

    template< class T01 >
    ConstructorArgForwarder(
        ArgPack< T01 > const& args
        )
        : Base( args.a01 )
    {}

    template< class T01, class T02 >
    ConstructorArgForwarder(
        ArgPack< T01, T02 > const& args
        )
        : Base( args.a01, args.a02 )
    {}

    template< class T01, class T02, class T03 >
    ConstructorArgForwarder(
        ArgPack< T01, T02, T03 > const& args
        )
        : Base( args.a01, args.a02, args.a03 )
    {}

    // And more, up to max 12 arguments.
};

Он, в свою очередь, использует класс пакета аргументов ArgPack (хорошо, шаблон класса), который может выглядеть следующим образом:

enum NoArg {};

template<
    class T01 = NoArg, class T02 = NoArg, class T03 = NoArg,
    class T04 = NoArg, class T05 = NoArg, class T06 = NoArg,
    class T07 = NoArg, class T08 = NoArg, class T09 = NoArg,
    class T10 = NoArg, class T11 = NoArg, class T12 = NoArg
    >
struct ArgPack;

template<
    >
struct ArgPack<
    NoArg, NoArg, NoArg, NoArg, NoArg, NoArg,
    NoArg, NoArg, NoArg, NoArg, NoArg, NoArg
    >
{};

typedef ArgPack<
    NoArg, NoArg, NoArg, NoArg, NoArg, NoArg,
    NoArg, NoArg, NoArg, NoArg, NoArg, NoArg
    >                                           EmptyArgPack;

inline ArgPack<> args() { return ArgPack<>(); }

template<
    class T01
    >
struct ArgPack<
    T01, NoArg, NoArg, NoArg, NoArg, NoArg,
    NoArg, NoArg, NoArg, NoArg, NoArg, NoArg
    >
{
    T01 const&  a01;
    ArgPack( T01 const& v01 )
        : a01( v01 )
    {}
};

template< class T01 >
inline ArgPack< T01 >
args( T01 const& a01 )
{
    return ArgPack< T01 >( a01 );
}

Отказ от ответственности: ошибки, возможно, только что проникли, например, в копировании кода из моего блога. Тем не менее, это сработало в то время, когда я об этом писал, в мае 2010 года.

Примечание. Как я уже говорил в последних двух постах в блоге о клонировании, есть три основных общих способа сделать это, и из них простой макрос удары два других с хорошим запасом, для C ++ 03. Тем не менее, с C ++ 11 подход «посредник», который вы выбрали здесь, кажется лучше. «Боковое наследование» через доминирование просто сложно и неэффективно, но если вы ограничены C ++ 03, тогда рассмотрите простой макрос!

Примечание 2: В прошлый раз, когда я предложил заняться практичным и разумным делом, меня сильно опровергли (по-видимому, дети из Reddit). С тех пор, однако, я перестал заботиться о точках повторения SO, и в частности о отрицательных результатах. Так что, к счастью, теперь я снова могу дать хороший совет, как в старые времена Usenet, просто игнорируя их бездумную реакцию детей на некоторые слова. : -)

...