在我写的简单解析器库中,使用std::tuple_cat
组合了多个解析器的结果。但是当应用一个多次返回相同结果的解析器时,将这个元组转换为像vector或deque这样的容器变得很重要。
如何做到这一点?如何将std::tuple<A>
,std::tuple<A, A>
,std::tuple<A, A, A>
等任何元组转换为std::vector<A>
?
我认为使用typename ...As
和sizeof ...(As)
可能会有这种情况,但我不确定如何创建一个较小的元组来递归调用该函数。或者如何编写迭代解决方案,逐个从元组中提取元素。 (因为std::get<n>(tuple)
是在编译时构造的。)
怎么做?
答案 0 :(得分:14)
随着std::apply()
的引入,这非常简单:
include(dirname(__FILE__)). DIRECTORY_SEPARATOR.'project'.DIRECTORY_SEPARATOR.'b.php';
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));
}
是一个C ++ 17函数,但可以在C ++ 14中实现(参见可能实现的链接)。作为改进,您可以添加SFINAE或std::apply()
,其中元组中的所有类型实际上都是static_assert
。
正如T.C.指出的那样,这会产生每个元素的额外副本,因为T
由std::initializer_list
数组支持。那是不幸的。我们赢得一些不必对每个元素进行边界检查,但在复制上丢失一些。复制最终过于昂贵,另一种实现方式是:
const
有关扩展器技巧的说明,请参阅this answer。请注意,我删除了前导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));
}
,因为我们知道包是非空的。使用C ++ 17,使用fold-expression变得更清晰:
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));
构造函数更好。不幸的。
答案 1 :(得分: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;
}
答案 2 :(得分: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 功能。更多信息here