C ++转换std :: tuple< A,A,A ...>到std :: vector或std :: deque [英] C++ Transform a std::tuple<A, A, A...> to a std::vector or std::deque

查看:60
本文介绍了C ++转换std :: tuple< A,A,A ...>到std :: vector或std :: deque的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我正在编写的简单解析器库中,使用 std :: tuple_cat 组合了多个解析器的结果.但是,当应用多次返回相同结果的解析器时,将这个元组转换为向量或双端队列之类的容器就变得很重要.

In the simple parser library I am writing, the results of multiple parsers is combined using std::tuple_cat. But when applying a parser that returns the same result multiple times, it becomes important to transform this tuple into a container like a vector or a deque.

这怎么办? std :: tuple< A> std :: tuple< A,A> std :: tuple< A,A,A> 等转换为 std :: vector< A> ?

How can this be done? How can any tuple of the kind std::tuple<A>, std::tuple<A, A>, std::tuple<A, A, A> etc be converted into a std::vector<A>?

我认为使用 typename ... As sizeof ...(As)可能是可行的,但是我不确定如何创建一个较小的元组来递归调用该函数.或者如何编写一个迭代解决方案,该解决方案从元组中一个接一个地提取元素.(因为 std :: get< n>(元组)是在编译时构造的.)

I think this might be possible using typename ...As and sizeof ...(As), but I am not sure how to create a smaller tuple to call the function recursively. Or how to write an iterative solution that extracts elements from the tuple one by one. (as std::get<n>(tuple) is constructed at compile-time).

该怎么做?

推荐答案

引入了 std :: apply() ,这非常简单:

With the introduction of std::apply(), this is very straightforward:

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 .

std::apply() is a C++17 function but is implementable in C++14 (see link for possible implementation). As an improvement, you could add either SFINAE or a static_assert that all the types in the Tuple are actually T.

TC 指出,这会产生每个元素的额外副本,因为 std :: initializer_list const 数组支持.那真不幸.我们赢得了一些好处,因为不必对每个元素进行边界检查,但是在复制方面却失去了一些.复制最终过于昂贵,一种替代实现是:

As T.C. points out, this incurs an extra copy of every element, since std::initializer_list is backed by a const array. That's unfortunate. We win some on not having to do boundary checks on every element, but lose some on the copying. The copying ends up being too expensive, an alternative implementation would be:

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,它可以通过折叠表达式变得更加简洁:

See this answer for an explanation of the expander trick. Note that I dropped the leading 0 since we know the pack is non-empty. With C++17, this becomes cleaner with a fold-expression:

    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 构造函数好.不幸的.

Although still relatively not as nice as the initializer_list constructor. Unfortunate.

这篇关于C ++转换std :: tuple&lt; A,A,A ...&gt;到std :: vector或std :: deque的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆