2012-03-01 11 views
0

CRTPを使用するテンプレートクラスを使用して、複数のレベルの継承を可能にする2番目のテンプレートパラメータBaseを使用して、クローンパターンを実装しています。間接的な基底クラスのコンストラクタを呼び出そうとするとコンパイラエラーが発生します。CRTPを使用した転送コンストラクタ

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)はそれらをまだ実装されていません。

+0

コンパイラエラーとは何ですか? –

+0

AFAIKあなたのコンパイラにはそれがありませんが、継承されたコンストラクタは、この場合可変長の完璧なコンストラクタよりも優先される可能性があります。これは、 'Clonable'定義で' Using Base :: Base; 'のようになります。ヘッドアップと同じように! –

答えて

6

クローン "ミドルマン"クラスの前方コンストラクタ引数、またはより良い(Luc Dantonがこれを示唆した)C++ 11コンストラクタ継承を使用する必要があります。

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

ヘルパー引数フォワーダクラスを使用してC++ 03で行う方法の1つは、私の古いブログの投稿"3 ways to mix in a generic cloning implementation"で議論されています。そして、仲介(クローニング実装)クラスは次のようになります。

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

は免責事項:erorsはちょうど潜入していることを例えば私のブログからコードをコピーする際に。しかし、それについては、2010年5月に投稿した時に働いた。

注:上記の2つのブログの投稿の最後で、クローンについては、3つのの一般的な方法、これらのうち、単純なマクロは、C++ 03の場合、他の2つのマージンを上回ります。しかし、C++ 11では、ここで選択した「仲介者」のアプローチが優れています。優位性を介して "横の継承"は複雑で非効率的ですが、C++ 03に制限されている場合は、単純なマクロを検討してください!

注2:私が実用的な&を知覚することを示唆した最後の時間は、私は大きく(おそらくRedditの子供たちによって)下降していました。しかし、それ以来、私はSOの担当者のポイント、特にダウンボントについて気にすることをやめました。だから、幸いなことに、古いユーズネットの時と同じように、良いアドバイスをやり直すことができます。 :-)

+0

私はたぶん少なくともC++ 11のサポートがより良いコンパイラを得るまで、マクロを使用することになります。私は実際にプロジェクトを呼び出すときにCRTPを試してみる機会を得たので、実際には考えていなかった。 – Lucretiel

+0

私はCRTPクローン可能なソリューションを試すのに1〜2日を費やしていたが、やり方この答えが記述されているように動作するとしても、それをそうする価値があるかどうかはわかりません。おそらく手動で 'クローン'機能を悲しいように手動で作成することにもなります。 – user1633272

関連する問題