2016-04-09 7 views
11

テンプレートにT1とT2という2つの引数があるとします。 T1自体がテンプレート化されたクラス(コンテナなど)であり、T2には何でも構いませんが、T1の基本テンプレートタイプを判断し、T2を引数として使用して再構築することは可能ですか?テンプレートをC++の引数から分けることはできますか?

たとえば、私がstd::vector<int>std::stringを受け取った場合、私は自動的にstd::vector<std::string>を作りたいと思うでしょう。しかし、私にstd::set<bool>doubleを与えた場合、それはstd::set<double>を生成するでしょう。

ここでtype_traits、関連するブログ、その他の質問を確認したところ、この問題を解決するための一般的なアプローチはありません。私が現在この作業を達成するために見ることができる唯一の方法は、T1として渡すことができる各タイプのテンプレートアダプタを構築することです。例えば

、私が持っていた場合:

template<typename T_inner, typename T_new> 
std::list<T_new> AdaptTemplate(std::list<T_inner>, T_new); 

template<typename T_inner, typename T_new> 
std::set<T_new> AdaptTemplate(std::set<T_inner>, T_new); 

template<typename T_inner, typename T_new> 
std::vector<T_new> AdaptTemplate(std::vector<T_inner>, T_new); 

私はdecltypeを使用して、私の問題を解決するために、演算子オーバーロードに頼ることができるはずです。

template <typename T1, typename T2> 
void MyTemplatedFunction() { 
    using my_type = decltype(AdaptTemplate(T1(),T2())); 
} 

何か不足していますか?より良いアプローチがありますか?

なぜこのようにしたいですか?

私は、ユーザーがモジュラーテンプレートを構築するために何をする必要があるか簡素化したいC++ライブラリを構築しています。たとえば、ユーザーがエージェントベースのシミュレーションを作成したい場合、生物型、母集団マネージャ、環境マネージャ、およびシステム管理者を持つWorldテンプレートを構成することができます。私はむしろ、多くのユーザーがNeuralNetworkAgentたびに繰り返す必要がたくありません

World< NeuralNetworkAgent, EAPop<NeuralNetworkAgent>, 
     MazeEnvironment<NeuralNetworkAgent>, 
     LineageTracker<NeuralNetworkAgent> > world; 

経営者の各々はまた、そう宣言は次のようになります、生物の種類を知っておく必要があります。私はテンプレート引数を変更することができました場合は、デフォルトの引数を使用することができ、上記のように単純化することができます。

World< NeuralNetworkAgent, EAPop<>, MazeEnvironment<>, LineageTracker<> > world; 

プラスそれはタイプミスを気にせずに別の世界タイプから変換する方が簡単です。もちろん

、私はstatic_assertを使用して、ほとんどのエラーに対処し、ちょうど長い宣言に対処するが、私はよりよい解決策が可能であるかどうかを知りたいのですができます。

#include <vector> 
#include <string> 

template<typename T, typename ...U> class AdaptTemplateHelper; 

template<template <typename...> class T, typename ...V, typename ...U> 
class AdaptTemplateHelper<T<V...>, U...> { 
public: 

    typedef T<U...> type; 
}; 

template<typename T, typename ...U> 
using AdaptTemplate=typename AdaptTemplateHelper<T, U...>::type; 

void foo(const std::vector<std::string> &s) 
{ 
} 

int main() 
{ 
    AdaptTemplate<std::vector<int>, std::string> bar; 

    bar.push_back("AdaptTemplate"); 
    foo(bar); 
    return 0; 
} 

ベストC++の質問今週:

+0

私が得た3つの答えはすべて優れていました。 @Barryのものが最も徹底的で、T.C.私の根底にある問題を最も良く解決していますが、私が受け入れたSam Varshavchik(私が受け入れた)の回答は、私が尋ねたように質問を解決するための最もエレガントなものだと思います。みなさんありがとう! –

+0

この投稿は重複しているので、@Ben Voightが指摘したものは非常に似ていますが、特にSTLに重点を置いていると思います。問題のテンプレートがすべて同じライブラリから来た場合は、確かに可能な余分なトリックがあります。それは、もう一つの質問にも面白くて有用な答えがあるということです。それは、ここで提供された回答が、この質問をよりターゲットとし、すぐに役立つことが分かりました。 –

+0

他の質問はそれほど広範囲ではありませんが、答えはあなたのケースを完全にカバーしています。だからこそ私の旗は、「あなたの質問には既に答えがあります」というバナーが表示されます。 –

答えて

3

これは、GCC 5.3.1でテストについて、あなたは求めている方法で動作しているようです。

+0

私は微妙なメタプログラミングの問題を扱い、C++の境界を知るのが快適になると思うたびに、私はここで質問し、全く新しいことを学びます。ありがとう! –

3

これは基本的に2つの別々の問題である:どのようにクラステンプレートにクラステンプレートのインスタンス化を分解し、その後、クラステンプレートを取り、それをインスタンス化する方法。すべてが常にタイプであるならば、テンプレートメタプログラミングがより簡単であるという原則を考えてみましょう。

まず、2番目の部分。クラステンプレートを考えると、のは、メタ関数クラスにそれを回すみましょう:

ここ
template <template <typename...> class F> 
struct quote { 
    template <typename... Args> 
    using apply = F<Args...>; 
}; 

quote<std::vector>はメタ関数クラスです。これは、メンバテンプレートapplyを持つ具象型です。だからquote<std::vector>::apply<int>std::vector<int>です。

ここで、型を解凍する必要があります。それをunquote(少なくとも私にとっては適切だと思う)と呼ぼう。これは、型を取ると、メタ関数クラスを生成するメタ関数である:

template <class > 
struct unquote; 

template <class T> 
using unquote_t = typename unquote<T>::type; 

template <template <typename...> class F, typename... Args> 
struct unquote<F<Args...>> { 
    using type = quote<F>; 
}; 

は、今あなたがする必要があるすべてはunquoteにインスタンス化を渡し、あなたはそれを吐き出すメタ関数クラスにする新しい引数を提供します:

あなたの特定のケースについては
unquote_t<std::vector<int>>::apply<std::string> 

、ちょうどquoteすべて:

// I don't know what these things actually are, sorry 
template <class Agent, class MF1, class MF2, class MF3> 
struct World { 
    using t1 = MF1::template apply<Agent>; 
    using t2 = MF2::template apply<Agent>; 
    using t3 = MF3::template apply<Agent>; 
}; 


World< NeuralNetworkAgent, 
    quote<EAPop>, 
    quote<MazeEnvironment>, 
    quote<LineageTracker> 
> w; 
+1

このアプローチの問題は、デフォルト以外のアロケータ、比較器などを使用できないことです。 –

+0

@ T.C。とにかくスワップするにはコンパレータが疑問ですが、コンパレータがテンプレートでない場合はどうすればよいですか?それを明示的に提供する方が良い。アロケータでは、確かに、別の種類の 'quote'を書くことができます。それは大きな問題ではありません。 – Barry

+1

@ T.C。他のソリューションと同じコメントはありませんか? – Barry

3

あなたの実際の問題はテンプレートテンプレートパラメータを取るだけで解決できます。バリーのquote @

template <class Agent, template<class...> class F1, 
         template<class...> class F2, 
         template<class...> class F3> 
struct World { 
    // use F1<Agent> etc. 
}; 

World<NeuralNetworkAgent, EAPop, MazeEnvironment, LineageTracker > world; 

は、より複雑なメタプログラミングのために有用であるこれを行うには手の込んだ方法で、ですが、この単純なユースケースのためのIMO過剰です。

C++では、任意のテンプレート特殊化を別のテンプレート引数セットにリビルドすることはできません。多くの場合、サブセット(主に型パラメータのみを取り入れたテンプレート、サポートすることを選択できる他の組み合わせ)を扱うことができます。それでも多くの問題があります。 std::unordered_set<int, my_fancy_hash<int>, std::equal_to<>, std::pmr::polymorphic_allocator<int>>を正しく再バインドするには、使用するテンプレートに固有の知識が必要です。

関連する問題