2017-10-27 4 views
1

std::tupleの各要素に対して操作を実行する関数を記述したいが、その要素が特定の型のものであれば、それが別のタイプのものであれば、別のことをするでしょう。私はすべての要素に同じことを行うことができ、コードがあります。タイプによってタプル要素の特殊な関数を作成する方法

template<std::size_t I = 0, class ...Ts> 
inline typename std::enable_if<I == sizeof...(Ts), void>::type 
tupel_el_func(std::tuple<Ts...> &t) { } // base case: do nothing 

template<std::size_t I = 0, class... Ts> 
inline constexpr typename std::enable_if<I < sizeof...(Ts), void>::type 
tupel_el_func(std::tuple<Ts...> &t) { 

    auto el = std::get<I>(t); 
    // do thing with el 
    tupel_el_func<I+1, Ts...>(t); 
} 

を今私は、特定のタイプ(例えばタイプchar*)のある要素に対して異なることを行うために、この機能を特化する必要があります。私はそうのようなstd::enable_ifを使用して試してみましたが、それはコンパイルされません。

template<int N, typename... Ts> 
using NthTypeOf = typename std::tuple_element<N, std::tuple<Ts...>>::type; 

template<std::size_t I = 0, class... Ts> 
inline constexpr typename std::enable_if<I < sizeof...(Ts) && std::is_same<char*, NthTypeOf<I, Ts...>>::value, void>::type 
tupel_el_func(std::tuple<Ts...> &t) { 
    // function body for when get<I>(t) is a char* 
} 

template<std::size_t I = 0, class... Ts> 
inline constexpr typename std::enable_if<I < sizeof...(Ts) && !std::is_same<char*, NthTypeOf<I, Ts...>>::value, void>::type 
tupel_el_func(std::tuple<Ts...> &t) { 
    // function body for generic next element 
} 

は助けてくれてありがとう。

+1

Not C++ 14?それは良くないね。 – Yakk

+0

ええ、C++ 11でない必要があります14 –

+0

なぜC++ 11ですか?どのコンパイラがサポートする必要がありますか?多くの人にC++ 14の機能がいくつかありました。 – Yakk

答えて

0

あなたは種類ごとに特定のケースのためのロジックを実装し、専門/汎用的な機能を持つ要素の処理機能のロジックを記述することができます

template<std::size_t I = 0, class ...Ts> 
inline typename std::enable_if<I == sizeof...(Ts), void>::type 
tupel_el_func(std::tuple<Ts...> &t) { } // base case: do nothing 

template<std::size_t I = 0, class... Ts> 
inline constexpr typename std::enable_if<I < sizeof...(Ts), void>::type 
tupel_el_func(std::tuple<Ts...> &t) { 
    do_logic(get<I>(t)); 
    tupel_el_func<I+1>(t); 
} 

、その後、そのような専用/汎用のdo_logicの機能があります。

template <class T> 
void do_logic(T element) { 
    // generic logic 
} 

template <> 
void do_logic(char* element) { 
    // char* logic 
} 
+0

これは私のために働いた(私の問題を完全に解決した)ので、なぜdownvote –

+0

あなたはベースケースの再帰を持っています。 –

+0

質問文の基本ケースではありませんか? –

1

私は再帰を完全に回避することを提案します。

悪い点は、C++ 11を使用しているため、std::index_sequencestd::make_index_sequenceのようなC++ 14の機能を使用できないという点です。

しかし、簡単にシミュレートできます。今

template <std::size_t ...> 
struct indexSequence 
{ }; 

template <std::size_t N, std::size_t ... Next> 
struct indexSequenceHelper 
{ using type = typename indexSequenceHelper<N-1U, N-1U, Next ... >::type; }; 

template <std::size_t ... Next> 
struct indexSequenceHelper<0U, Next ... > 
{ using type = indexSequence<Next ... >; }; 

template <std::size_t N> 
using makeIndexSequence = typename indexSequenceHelper<N>::type; 

との例では、汎用的なstd::tupleオブジェクトを受け取る機能foo()を書き、fooH()はタプルを渡すと、rangeにヘルパー関数を呼び出して、AのためにゼロからN-1へのインデックスのリスト(することができますstd::tuple)types` N

template <typename ... Ts> 
void foo (std::tuple<Ts...> const & t) 
{ fooH(t, makeIndexSequence<sizeof...(Ts)>{}); } 

次に、再帰とstd::getとインデックスを使用せずに、あなたはタプル内のすべての要素の上、実行するためのヘルパー関数、機能を実装することができbar()

template <typename ... Ts, std::size_t ... Is> 
void fooH (std::tuple<Ts...> const & t, indexSequence<Is...> const &) 
{ 
    using unused = int[]; 

    (void)unused { 0, ((void)bar(std::get<Is>(t)), 0)... }; 
} 

今あなたが望むように、(次の例では、longのために)特定のタイプのために、汎用テンプレートにbar()機能

template <typename T> 
void bar (T const & t) 
{ std::cout << "- generic bar(): " << t << std::endl; } 

となど、多くの特定のオーバーロードされたバージョンを開発

void bar (long l) 
{ std::cout << "- bar(), long version: " << l << std::endl; } 

以下は完全なC++ 11の動作例です

#include <tuple> 
#include <iostream> 

template <std::size_t ...> 
struct indexSequence 
{ }; 

template <std::size_t N, std::size_t ... Next> 
struct indexSequenceHelper 
{ using type = typename indexSequenceHelper<N-1U, N-1U, Next ... >::type; }; 

template <std::size_t ... Next> 
struct indexSequenceHelper<0U, Next ... > 
{ using type = indexSequence<Next ... >; }; 

template <std::size_t N> 
using makeIndexSequence = typename indexSequenceHelper<N>::type; 

template <typename T> 
void bar (T const & t) 
{ std::cout << "- generic bar(): " << t << std::endl; } 

void bar (long l) 
{ std::cout << "- bar(), long version: " << l << std::endl; } 

template <typename ... Ts, std::size_t ... Is> 
void fooH (std::tuple<Ts...> const & t, indexSequence<Is...> const &) 
{ 
    using unused = int[]; 

    (void)unused { 0, ((void)bar(std::get<Is>(t)), 0)... }; 
} 

template <typename ... Ts> 
void foo (std::tuple<Ts...> const & t) 
{ fooH(t, makeIndexSequence<sizeof...(Ts)>{}); } 

int main() 
{ 
    std::tuple<short, int, long, long long> t { 0, 1, 2L, 3LL }; 

    foo(t); 
} 
+1

単にindex_sequenceとmake_index_sequenceを正確に実装するほうがよいでしょう。したがって、OP a)それらの使用方法を学びます。b)14に切り替えると、実装を削除できます。 –

+0

@NirFriedman - まあ...私は正確には*同じ名前を使用するのは良い考えだとは思わないが...良い点;いつものように私の名前の選択は恐ろしいです。より良い(私が望む)名前で修正された回答。 – max66

関連する問題