2017-02-27 85 views
6

在我寫的簡單解析器庫中,多個解析器的結果使用std::tuple_cat進行組合。但是,當應用多次返回相同結果的解析器時,將這個元組轉換爲像vector或deque這樣的容器變得非常重要。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),但我不知道如何創建一個較小的元組來遞歸調用函數。或者如何編寫一個逐個從元組中提取元素的迭代解決方案。 (因爲編譯時構建了std::get<n>(tuple))。

如何做到這一點?

+0

[迭代過元組]的可能的複製(http://stackoverflow.com/questions/1198260/iterate-over-tuple) – filmor

+1

完全不同。更好地與[索引技巧](http://loungecpp.wikidot.com/tips-and-tricks:indices)接觸。 – Xeo

回答

3

這裏有一個辦法做到這一點:

#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

通過引入std::apply(),這是非常簡單的:

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,Tuple中的所有類型實際上都是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。用C++ 17,這變得與摺疊表達清潔器:

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構造函數。不幸的。

+0

加1不僅是偉大的答案,但主要是爲了斷言的建議。 –

+0

我唯一不喜歡的是初始化器列表,它是每個元素的額外拷貝。 –

+0

@ T.C。哦,你是對的。我沒有想到這一點。這是不幸的...我猜有'reserve()'/'push_back()'? – Barry

相關問題