[英]C++ Transform a std::tuple<A, A, A…> to a std::vector or std::deque
在我正在编写的简单解析器库中,使用std::tuple_cat
组合多个解析器的结果。 但是当应用多次返回相同结果的解析器时,将这个元组转换为一个容器(如向量或双端队列)变得很重要。
如何才能做到这一点? 如何将std::tuple<A>
、 std::tuple<A, A>
、 std::tuple<A, A, A>
等类型的std::tuple<A, A, A>
转换为std::vector<A>
?
我认为这可能使用typename ...As
和sizeof ...(As)
,但我不确定如何创建一个较小的元组来递归调用该函数。 或者如何编写一个迭代解决方案,从元组中一个一个地提取元素。 (因为std::get<n>(tuple)
是在编译时构造的)。
这该怎么做?
随着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
以确保元组中的所有类型实际上都是T
。
正如TC指出的那样,这会导致每个元素的额外副本,因为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));
}
有关扩展器技巧的解释,请参阅此答案。 请注意,我删除了前导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
构造函数那么好。 不幸的。
这是一种方法:
#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;
}
虽然,这并不能完全回答问题,但在某些情况下这仍然可能是合适的。 只有当元组中的元素数量在 5、6 左右时。(你知道大小)。
tuple<int, int, int, int> a = make_tuple(1, 2, 3, 4);
auto [p, q, r, s] = a;
vector<int> arr(p, q, r, s); // Now, arr has the same elements as in tuple a
请注意,这是 C++ 17 特性。 更多信息 在这里
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.