C++将std :: tuple 转换为std :: vector或std :: deque
在我写的简单解析器库中,多个解析器的结果使用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 ...As
和sizeof ...(As)
,但我不知道如何创建一个较小的元组来递归调用函数。或者如何编写一个逐个从元组中提取元素的迭代解决方案。 (因为编译时构建了std::get<n>(tuple)
)。
如何做到这一点?
这里有一个办法做到这一点:
#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;
}
通过引入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_list
由const
阵列的支持。那真不幸。我们赢得了一些不必对每个元素进行边界检查,但在复制上失去一些。复制结束是太贵了,一个替代的实现是:
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
构造函数。不幸的。
加1不仅是伟大的答案,但主要是为了断言的建议。 –
我唯一不喜欢的是初始化器列表,它是每个元素的额外拷贝。 –
@ T.C。哦,你是对的。我没有想到这一点。这是不幸的...我猜有'reserve()'/'push_back()'? – Barry
[迭代过元组]的可能的复制(http://stackoverflow.com/questions/1198260/iterate-over-tuple) – filmor
完全不同。更好地与[索引技巧](http://loungecpp.wikidot.com/tips-and-tricks:indices)接触。 – Xeo