2017-02-27 84 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

相关问题