2017-02-27 10 views
6

私が書いている単純なパーサライブラリでは、複数のパーサの結果がstd::tuple_catを使って結合されています。しかし、同じ結果を何度も返すパーサを適用するときには、このタプルをベクトルまたは両端キューのようなコンテナに変換することが重要になります。C++ std :: tuple <A, A, A...>をstd :: vectorまたはstd :: dequeに変換します。

どうすればいいですか? std::tuple<A>std::tuple<A, A>std::tuple<A, A, A>などのタプルはどのようにstd::vector<A>に変換できますか?

これはtypename ...Assizeof ...(As)を使用している可能性がありますが、再帰的に関数を呼び出すための小さなタプルの作成方法はわかりません。または、タプルから要素を1つずつ抽出する反復解法を書く方法。 (コンパイル時にstd::get<n>(tuple)が作成されるため)。

これを行う方法?

+0

[繰り返しのタプル]の可能な複製(http://stackoverflow.com/questions/1198260/iterate-over-tuple) – filmor

+1

完全に異なる。 [indices trick](http://loungecpp.wikidot.com/tips-and-tricks:indices)でより良いアプローチが得られました。 – Xeo

答えて

3

ここでそれを行うための一つの方法だ:std::apply()の導入により

#include <tuple> 
#include <algorithm> 
#include <vector> 
#include <iostream> 

template<typename first_type, typename tuple_type, size_t ...index> 
auto to_vector_helper(const tuple_type &t, std::index_sequence<index...>) 
{ 
    return std::vector<first_type>{ 
     std::get<index>(t)... 
      }; 
} 

template<typename first_type, typename ...others> 
auto to_vector(const std::tuple<first_type, others...> &t) 
{ 
    typedef typename std::remove_reference<decltype(t)>::type tuple_type; 

    constexpr auto s = 
     std::tuple_size<tuple_type>::value; 

    return to_vector_helper<first_type, tuple_type> 
     (t, std::make_index_sequence<s>{}); 
} 

int main() 
{ 
    std::tuple<int, int> t{2,3}; 

    std::vector<int> v=to_vector(t); 

    std::cout << v[0] << ' ' << v[1] << ' ' << v.size() << std::endl; 
    return 0; 
} 
12

、これは非常に簡単です:

template <class Tuple, 
    class T = std::decay_t<std::tuple_element_t<0, std::decay_t<Tuple>>>> 
std::vector<T> to_vector(Tuple&& tuple) 
{ 
    return std::apply([](auto&&... elems){ 
     return std::vector<T>{std::forward<decltype(elems)>(elems)...}; 
    }, std::forward<Tuple>(tuple)); 
} 

std::apply()は、C++ 17の関数であるが、Cの+に実装可能です+14(可能な実装についてはリンクを参照)。改善のために、SFINAEまたはstatic_assertのいずれかを追加して、タプルのすべてのタイプが実際にTであるようにすることができます。


T.C.が指摘するようにstd::initializer_listconst配列によって補助されるので、これは、すべての要素の余分なコピーを招きます。それは残念です。私たちは、すべての要素について境界チェックを行う必要はなく、コピーでいくつかの要素を失います。コピーがあまりにも高価なってしまう、代替の実装は次のようになります。

template <class Tuple, 
    class T = std::decay_t<std::tuple_element_t<0, std::decay_t<Tuple>>>> 
std::vector<T> to_vector(Tuple&& tuple) 
{ 
    return std::apply([](auto&&... elems) { 
     using expander = int[]; 

     std::vector<T> result; 
     result.reserve(sizeof...(elems)); 
     expander{(void(
      result.push_back(std::forward<decltype(elems)>(elems)) 
      ), 0)...}; 
     return result; 
    }, std::forward<Tuple>(tuple)); 
} 

はエキスパンダートリックの説明についてはthis answerを参照してください。パックが空でないことがわかっているので、先頭の0を削除しました。

return std::apply([](auto&&... elems) { 
     std::vector<T> result; 
     result.reserve(sizeof...(elems)); 
     (result.push_back(std::forward<decltype(elems)>(elems)), ...); 
     return result; 
    }, std::forward<Tuple>(tuple)); 

initializer_listコンストラクタとしてまだ比較的ように素敵ではないが:C++ 17で、これは折り畳み式できれいになります。残念です。

+0

プラス1は偉大な答えだけでなく、主にアサルトのアドバイスのためです。 –

+0

私が嫌いなのは、イニシャライザのリストだけです。要素ごとに1つの余分なコピーです。 –

+0

@ T.C。ああ、あなたは正しい。私はそれを考えなかった。それは残念です...私は 'reserve()'/'push_back()'があると思いますか? – Barry

関連する問題