1

CRTP を使用してテンプレート クラスを使用してクローン パターンを実装し、2 番目のテンプレート パラメーター 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) はまだそれらを実装していません。

4

1 に答える 1

7

クローン作成の「middleman」クラスにコンストラクター引数を転送させる必要があります。または、C ++ 11コンストラクターの継承を使用することをお勧めします(Luc Dantonがこれを提案しました)。

したがって、C ++ 11でこれを行うのは簡単ですが、C ++ 03や、VisualC++などのC++11引数転送やコンストラクター継承をまだサポートしていない現在のコンパイラではそれほど簡単ではありません。 10.10。

ヘルパー引数フォワーダークラスを使用してC++03でこれを行う1つの方法は、「一般的なクローン作成の実装を組み合わせる3つの方法」を投稿した私の古いブログで説明されています。その場合、仲介者(クローン実装)クラスは次のようになります。

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

ConstructorArgForwarder以前のブログ投稿でC++03互換について説明しました。次のようになります。

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(まあOK、クラステンプレート)を使用します。これは次のようになります。

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年5月に投稿した時点では機能していました。

注:上記の2つのブログ投稿の最後でクローン作成について説明したように、クローン作成を行うための3つの主な一般的な方法があります。これらのうち、 C ++ 03の場合、単純なマクロが他の2つを十分に上回ります。ただし、C ++ 11では、ここで選択した「仲介者」アプローチの方が優れているようです。ドミナントによる「横向きの継承」は複雑で非効率的ですが、C ++ 03に制限されている場合は、単純なマクロを検討してください。

注2:私が最後に実用的で賢明なことをすることを提案したとき、私は(おそらくRedditの子供たちによって)ひどく反対されました。しかし、それ以来、私はSO担当者ポイント、特に反対票を気にすることをやめました。ですから、幸いなことに、昔のUsenetの時代と同じように、特定の言葉に対する子供たちの無意識の反応を無視して、再び良いアドバイスをすることができます。:-)

于 2012-03-01T17:27:14.910 に答える