繁体   English   中英

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

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

如何才能做到这一点? 如何将std::tuple<A>std::tuple<A, A>std::tuple<A, A, A>等类型的std::tuple<A, A, A>转换为std::vector<A>

我认为这可能使用typename ...Assizeof ...(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_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));
}

有关扩展器技巧的解释,请参阅此答案 请注意,我删除了前导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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM