2016-04-16 3 views
2

私は、(同じ概念を共有する)一緒に任意の順序で動作する多くのテンプレートクラスを持っています。ネストされたテンプレートクラスを初期化する

template<typename T> 
class A { 
    T t_; 
public: 
    void call() { 
    // Do something. 
    t_.call(); 
    } 
}; 

template<typename T, typename U> 
class B { 
    T t_; 
    U u_; 
public: 
    void call() { 
    // Do something. 
    t_.call(); 
    // Do something. 
    u_.call(); 
    } 
}; 

class C { 
public: 
    void call() { 
    // Do something. 
    } 
}; 

をそして私は、次のインスタンス化されたクラスがあります:

私が持っていると仮定すると

using Foo = A<B<A<C>,C>>; 

は、Cは、おそらく動作するように特別なコンストラクタ(または初期化関数)を必要とする、と仮定します。私が実行時に知っているだけのこと。

struct C { 
    void init(int); 
    void call(); 
}; 

おそらくFooを初期化できますか?または他のネストされたクラスの組み合わせですか?

template<typename I> 
struct C { 
    C() : var_(I::get()) 
    void call(); 
}; 

そして、関数の内部ではFooを作成します:

が私の現在の回避策としてCを定義することである

あなたが見
int main() 
{ 
    int i = 0; 

    struct HelperC1 { 
    static int get(bool set = false, int value = 0) { 
     static int value_ = value; 
     if (set) value_ = value; 
     return value_; 
    } 
    } helperC1; 

    struct HelperC2 { 
    static int get(bool set = false, int value = 0) { 
     static int value_ = value; 
     if (set) value_ = value; 
     return value_; 
    } 
    } helperC2; 

    helperC1.get(true, i); 
    helperC2.get(true, i+1); 

    A<B<A<C<HelperC1>>,C<HelperC2>>> foo; 
    foo.call(); 

    return 0; 
} 

Live example.

、この回避策は非常に便利ではありません。別のアプローチは、引数ではFooの最初のコンストラクタを呼び出し、Cにリダイレクトすることですが、これはのような別のクラスの組み合わせのために非常に悪いです:

using Bar = A<B<A<C>,<B<B<A,C>,C>>>; 

質問:どのように実行時にネストされた(テンプレート)のクラスを初期化します議論(より良い/より良い、よりクリーンな方法で)?

+0

は "おそらく" あなたは "適切に" を意味するのですか? –

+0

あなたのクラス 'A'と' B'(そしておそらくゲッター)にコンストラクタを与えないのはなぜでしょうか? – Jarod42

答えて

1

この例では、移動に構成されたテンプレートの種類を分岐し、ヘルパー関数を転送するための値を構築します。

フォワーディングヘルパーは、複雑なパラメータで各コンストラクタを指定する必要性を避けるために、型推論を使用してコンストラクタをラップします。

この例では、call()関数は、オブジェクトの子のパラメータ構造と値をダンプします。

#include <iostream> 
#include <utility> 

template<typename T> 
class A { 
    T t; 
public: 
    A(T&& t) : t{std::move(t)} {} 
    void call() { 
     std::cout << "A<"; 
     t.call(); 
     std::cout << ">"; 
    } 
}; 
template <typename T> 
inline A<T> mkA(T&& t) { return A<T>{std::forward<T>(t)}; } 

template<typename T, typename U> 
class B { 
    T t; 
    U u; 
public: 
    B(T&& t, U&& u) : t{std::move(t)}, u{std::move(u)} {} 

    void call() { 
     std::cout << "B<"; 
     t.call(); 
     std::cout << ","; 
     u.call(); 
     std::cout << ">"; 
    } 
}; 
template <typename T, typename U> 
inline B<T,U> mkB(T&& t, U&& u) { 
    return B<T,U>{std::forward<T>(t), std::forward<U>(u)}; 
} 

class C { 
    int c; 
public: 
    C(int c) : c{c} {} 
    void call() { 
     std::cout << "C(" << c << ")"; 
    } 
}; 

int main() { 
    auto bar = mkA(mkB(mkA(C{1}), mkB(mkB(mkA(C{2}),C{3}), C{4}))); 
    bar.call(); 
    std::cout << '\n'; 
} 

この出力:によって

A<B<A<C(1)>,B<B<A<C(2)>,C(3)>,C(4)>>> 
2

すでに構築され、初期化されたオブジェクトを使用して、ポインタを使用してfooをビルドすることができます。すなわち:

テストされていないCODE

template<typename T> 
class A { 
    T* t_; 

public: 
    A(T* valptr) : t_(valptr){} 
    ~A(){ delete t_ ; } 

    void call() { 
    // Do something. 
    t_.call(); 
    } 
}; 

template<typename T, typename U> 
class B { 
    T* t_; 
    U* u_; 

public: 
    B(T* val1ptr, U* val2ptr):t_(val1ptr), u_(val2ptr){} 
    ~B(){delete val1ptr; delete val2ptr;} 

    void call() { 
    // Do something. 
    t_->call(); 
    // Do something. 
    u_->call(); 
    } 
}; 

class C { 
    private: 
    int x_; 
    public: 
    C(int x):x_(x){} 
    void call() { 
    // Do something. 
    } 
}; 

使用法:

A<B<A<C>,C>> foo(new B<A<C>,C>(new A<C>(new C(3)), new C(3))); 
+0

ポインタはなぜですか?あなたはそれらなしで同じものを得ることができます...あなたのクラスはもはやRAIIではありません...スコープ外になるコピーを作成すると、メンバポインタが削除され、クラスが壊れます。代わりにスマートポインタを使用する方が良いです。 – davidhigh

+0

@davidhighポインタ(または参照)なしで同じ動作を得る方法を示してください。仮想共通の親は、テンプレートではなくポインタを取り除くことを可能にする。 RAIIの場合、適切なコピーコンストラクタと代入演算子を実装する必要があります。暗黙的にメンバークラスを共有すると、ユースケースに応じて他のエラーが発生する可能性があります。 – xvan

関連する問題