2017-01-10 17 views
2

繰り返しを避けるためにテンプレートパラメータをまとめてバンドルする方法はありますか?C++でテンプレートパラメータを "バンドル"することはできますか?

私は、すべて同じ3つのテンプレートパラメータを使用するいくつかのクラスと関数を持っています。各クラス/関数を一度使用する関数を持つことは珍しいことではありません。結果として得られるコードは非常に厄介なものになります。 このコードを書くためのより簡潔な方法はありますか?

// ContextFactory is a pointer to functions that instantiate objects that are subtypes of MetricContext 
template<typename VertexID, typename EdgeMembershipType, typename SetBitmap> 
using ContextFactory = MetricContext <VertexID, EdgeMembershipType, SetBitmap> *(*)(const char *); 

template<typename VertexID, typename EdgeMembershipType, typename SetBitmap> 
    static vector<ContextFactory<VertexID, EdgeMembershipType, SetBitmap>> buildCFList() { 
     vector<ContextFactory<VertexID, EdgeMembershipType, SetBitmap>> answer; 
     answer.push_back(MetricContext<VertexID, EdgeMembershipType, SetBitmap>::template make<NeoContext<VertexID, EdgeMembershipType, SetBitmap >>); 
     return answer; 
    }; 

この機能のほぼ半分が文字列<VertexID, EdgeMembershipType, SetBitmap>>の繰り返しであるが、この文字列の各使用は、異なるクラスまたは関数に適用されますので、私は別名が動作するとは思わない注意してください。

(それが助け場合は、この機能の目的は、はい、これが可能であるMetricContext<VertexID, EdgeMembershipType, SetBitmap>>

+0

を助け

希望を示しています。あなたは、しかし、いくつかの可読性を失う。 – AndyG

+0

まず、戻り値として 'auto'を使うことができます。 – pSoLT

+0

FWIW:戻り値の型を 'auto'(' static auto buildCFList() ')に置き換えたとき、私はこのエラーを後の戻り値の型なしで返す。推定される戻り値の型はC++ 14の拡張子 ' – Zack

答えて

4

のサブタイプであるオブジェクトを作成する関数へのポインタの配列を作成することです。のは、保持するために少しのヘルパークラスを定義してみましょうタイプのリスト:

template <class... > struct pack { }; 

そしてpackの内側に何とテンプレートをインスタンス化メタ関数:

template <template <class... > class T, class P> 
struct unpack_; 

template <template <class... > class T, class... P> 
struct unpack_<T, pack<P...>> { 
    using type = T<P...>; 
}; 

template <template <class... > class T, class P> 
using unpack = typename unpack_<T, P>::type; 

そして、我々は今、私たちのパラメータパックを保存して使用することができます。

template <class A, class B, class C> 
struct Foo { }; 

using Params = pack<int, float, double>; 

unpack<Foo, Params> f; // f is a Foo<int, float, double> 

See it live on Coliru

+0

このアプローチの欠点は、関数内でパックを使用するという事実が失われているので、idが' buildCFList'実装の冗長性を減らすのに役立たないということです。 –

+0

@ Jean-BernardJansenもっと見ると、私はその質問を完全に間違って読んでいると思っています。 – Quentin

4

クエンティンさん@よりも、むしろ、より具体的なアプローチは、あなたのテンプレートは、単一のパラメータに依存することです - のためのtypedefを持つことが期待されていますVertexID,EdgeMembershipTypeおよびSetBitmapである。

// ContextFactory is a pointer to functions that instantiate objects that are 
// subtypes of MetricContext 
template<typename Types> 
using ContextFactory = MetricContext <Types> *(*)(const char *); 

template<typename Types> 
    static vector<ContextFactory<Types>> buildCFList() { 
     vector<ContextFactory<Types>> answer; 
     answer.push_back(MetricContext<Types>::template make<NeoContext<Types>>); 
     return answer; 
    }; 

注意あなたが実際にはtypedefでのいずれかを使用したい場合、あなたは、たとえば使用する必要があること:typename Types::VertexID

は(理想的には、テンプレート引数にTypesより良い名前を思い付くでしょう。)

1

あなたがC++ 11を使用している場合、あなたはに変数を結合するのstd ::タプルを利用することができます1。

同じことがあなたの質問に固有の問題について

template <typename A, typename B, typename C> 
void fn() { 

typedef std::tuple<A,B,C> myTuple; 

    myTuple tpl; 
    cout<<sizeof(std::get<0>(tpl))<<endl;; 
    cout<<sizeof(std::get<1>(tpl))<<endl; 
    cout<<sizeof(std::get<2>(tpl))<<endl; 

} 

int main() { 

fn<int,char,long>(); 

return 0; 
} 

することができ、あなたはこのように

template <typename A, typename B, typename C> 
void fn() { 

    using mycomb = std::tuple<A,B,C>; 

    vector<mycomb> v1; 
    v1.push_back(make_tuple(10,'c',20.0)); 
} 

としてタプルのベクトルを作成することができます理解するための単純な例として、あなたはしないでください同じことを繰り返す必要があります。タプルゲッター関数は、最初は少し厄介です。上記cout例はタプルパラメータにアクセスする方法をこれはあなたがテンプレートパラメータパックにそれらを束ねることができ、C++ 11では

+1

悲しいことに、重い重量のタプルがジェネリックバンドルとして使用されています。それはあまりにも多く行い、物事が遅すぎるようにします。 – Yakk