【问题标题】:How do I recurse tuples?如何递归元组?
【发布时间】:2016-02-16 21:36:47
【问题描述】:

我正在尝试制作一个可变参数模板容器,用于存储元素向量的元组。这个容器的要点是所有向量中的元素都是相关的,我想在以后保持这种相关性,但这对于计算来说不是必需的。想象一下,如果你愿意的话,一个 vector_3 和一个某种类型的 ref_id。

容器只会将向量统一变异在一起。所以我理解的部分看起来像这样:

template<typename ...Elems>
class container
{
    std::tuple<std::vector<Elems>...> data_;

public:
    template<typename I>
    const typename std::tuple_element<I, data_type>::type &nth_index() const
    { return std::get<I>(data_); }
};

我正在努力使用插入方法。我的想法是这样的:

void push_back(std::tuple<Elems...> &values)
{
    std::tuple<std::back_insert_iterator<std::vector<Elems>>...> inserters;
}

但我不知道如何初始化这个“插入器”元组。我一直在 stackoverflow 上查看各种递归模板示例,但我无法将其全部记在脑海中足够长的时间来理解它。

我假设如果我有这样一个元组,我可以使用简单的赋值:

inserters = values;

我还想为所有返回值元组的数组编写一个访问器:

std::tuple<Elems &...> operator[](const size_t index)
{
     ...
}

但我又不知道如何初始化这个元组。

我不可能是唯一一个想要这样做的人,而且我找不到学习它的好资源。同时,我正在尝试消化 0x 的原始可变参数模板提案。洞察力将不胜感激。我受到 MSVC 2012 实施的限制。

【问题讨论】:

    标签: c++ c++11 visual-c++-2012 stdtuple


    【解决方案1】:
    #include <vector>
    #include <tuple>
    #include <cstddef>
    #include <utility>
    
    template <typename... Elems>
    class container
    {
        using data_type = std::tuple<std::vector<Elems>...>;
    
        data_type data_;
    
    public:    
        template <std::size_t I>
        const typename std::tuple_element<I, data_type>::type& nth_index() const
        { return std::get<I>(data_); }
    
        void push_back(const std::tuple<Elems...>& values)
        {
            return push_back(std::make_index_sequence<sizeof...(Elems)>{}, values);
        }    
    
        std::tuple<Elems&...> operator[](std::size_t index)
        {
            return get_elems(std::make_index_sequence<sizeof...(Elems)>{}, index);
        }
    
    private:
        template <std::size_t... Is>
        void push_back(std::index_sequence<Is...>, const std::tuple<Elems...>& values)
        {
            using expand = int[];
            static_cast<void>(expand{ 0, (std::get<Is>(data_).push_back(std::get<Is>(values)), 0)... });
        }
    
        template <std::size_t... Is>
        std::tuple<Elems&...> get_elems(std::index_sequence<Is...>, std::size_t index)
        {
            return std::forward_as_tuple(std::get<Is>(data_)[index]...);
        }
    };
    

    DEMO

    【讨论】:

    • MSVC 2012 没有实现 std::integer_sequence (或 constexpr),所以我几乎没有修改就从 N3658 抢走了它。
    • 此外,您的扩展技巧让我大吃一惊,但它不适用于我的编译器。还有其他想法吗?
    • @MatthewReddington 试试int expand[] = { 0, (std::get&lt;Is&gt;(data_).push_back(std::get&lt;Is&gt;(values)), 0)... }; static_cast&lt;void&gt;(expand);
    • 还是不行,怪我的编译器版本;我知道下一个解决了很多缺点,我们只是还没有升级我们的项目。我会保留这个 sn-p 并在发生这种情况时尝试它。不过,谢谢。
    【解决方案2】:

    具有 SFINAE 和类型特征的 C++11 解决方案:

    template<typename ...Elems>
    class container {
      std::tuple<std::vector<Elems>...> data_;
    
      template<std::size_t N>
      typename std::enable_if<(N <std::tuple_size<decltype(data_)>::value), int>::type
      push_back_impl(std::tuple<Elems...> const &values) {
        std::get<N>(data_).push_back(std::get<N>(values));
        return push_back_impl<N + 1>(values);
      }
    
      template<std::size_t N>
      typename std::enable_if<(N == std::tuple_size<decltype(data_)>::value), int>::type
      push_back_impl(std::tuple<Elems...> const &values) {
        return 0;
      }
    
    public:
    
      void push_back(std::tuple<Elems...> const &values) {
         push_back_impl<0>(values);
      }
    
    };
    

    Live Demo

    至于下标运算符,您需要在 SO answer 中找到一些额外的机制:

    template <size_t ...I>
    struct index_sequence {};
    
    template <size_t N, size_t ...I>
    struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};
    
    template <size_t ...I>
    struct make_index_sequence<0, I...> : public index_sequence<I...> {};
    
    template<typename ...Elems>
    class container {
      std::tuple<std::vector<Elems>...> data_;
    
      template<size_t ...I>
      std::tuple<Elems&...> access_impl(std::size_t const idx, index_sequence<I...>) {
        return std::tie(std::get<I>(data_)[idx]...);
      }
    
    public:
    
      std::tuple<Elems&...> operator[](std::size_t const idx) {
        return access_impl(idx, make_index_sequence<sizeof...(Elems)>());
      }
    };
    

    Live Demo

    【讨论】:

    • 黑魔法,我的朋友。我将研究这个一段时间,试图了解所有正在发生的事情。这东西对我来说并不直观。你有学习通用和现代模板编程的资源吗?
    • @MatthewReddington 一个好的开始是 Bjarne 的The C++ Programming Language 4th edition。但大多数东西你会从 SO 和 cppreference 以及通过实验学到。
    猜你喜欢
    • 2014-03-05
    • 2021-12-15
    • 1970-01-01
    • 2015-06-27
    • 1970-01-01
    • 2017-10-08
    • 2013-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多