【问题标题】:Constructor arguments from tuple元组的构造函数参数
【发布时间】:2013-01-31 14:16:36
【问题描述】:

假设我有一个模板,它由一个类类型和许多参数类型参数化。与这些类型匹配的一组参数存储在一个元组中。如何将这些传递给类类型的构造函数?

几乎在 C++11 代码中:

template<typename T, typename... Args>
struct foo {
  tuple<Args...> args;
  T gen() { return T(get<0>(args), get<1>(args), ...); }
};

构造函数调用中的...不固定长度如何填充?

我想我可以想出一些复杂的递归模板调用机制来做到这一点,但我不敢相信我是第一个想要这个的人,所以我想会有现成的解决方案这个就在那里,甚至可能在标准库中。

【问题讨论】:

标签: c++ templates c++11 constructor stdtuple


【解决方案1】:

C++17 对此有 std::make_from_tuple

template <typename T, typename... Args>
struct foo
{
  std::tuple<Args...> args;
  T gen() { return std::make_from_tuple<T>(args); }
};

【讨论】:

    【解决方案2】:

    您需要一些模板元编程机制来实现这一点。

    实现参数分派的最简单方法是利用包含压缩编译时整数序列的表达式的包扩展。需要模板机制来构建这样的序列(有关标准化此类序列的提案的更多信息,请参见此答案末尾的备注)。

    假设有一个类(模板)index_range 封装了一个编译时整数范围 [M, N)和一个类(模板)index_list 封装了一个编译时整数列表,这就是你会使用它们:

    template<typename T, typename... Args>
    struct foo
    {
        tuple<Args...> args;
    
        // Allows deducing an index list argument pack
        template<size_t... Is>
        T gen(index_list<Is...> const&)
        {
            return T(get<Is>(args)...); // This is the core of the mechanism
        }
    
        T gen()
        {
            return gen(
                index_range<0, sizeof...(Args)>() // Builds an index list
                );
        }
    };
    

    这是index_rangeindex_list的可能实现:

    //===============================================================================
    // META-FUNCTIONS FOR CREATING INDEX LISTS
    
    // The structure that encapsulates index lists
    template <size_t... Is>
    struct index_list
    {
    };
    
    // Collects internal details for generating index ranges [MIN, MAX)
    namespace detail
    {
        // Declare primary template for index range builder
        template <size_t MIN, size_t N, size_t... Is>
        struct range_builder;
    
        // Base step
        template <size_t MIN, size_t... Is>
        struct range_builder<MIN, MIN, Is...>
        {
            typedef index_list<Is...> type;
        };
    
        // Induction step
        template <size_t MIN, size_t N, size_t... Is>
        struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
        {
        };
    }
    
    // Meta-function that returns a [MIN, MAX) index range
    template<unsigned MIN, unsigned MAX>
    using index_range = typename detail::range_builder<MIN, MAX>::type;
    

    另外请注意,Jonathan Wakelyinteresting proposal 存在用于标准化 int_seq 类模板,这与我在这里称为index_list 的内容非常相似。

    【讨论】:

    • 我还没有尝试过这个,但在我看来,你有一个错误:你将MAX设置为sizeof...(Args) - 1,即使该最大值不包括在列表。否则,感谢您的出色回答,因为它非常清楚地解释了各个步骤。
    • 主模板的前向声明毫无意义且令人困惑。只需先定义“归纳步骤”即可。
    【解决方案3】:

    使用index_sequence 解压缩std::tuple(或std::pairstd::array,或其他任何支持元组接口的东西):

    #include <utility>
    #include <tuple>
    
    template <typename Tuple, std::size_t... Inds>
    SomeClass help_make_SomeClass(Tuple&& tuple, std::index_sequence<Inds...>)
    {
        return SomeClass(std::get<Inds>(std::forward<Tuple>(tuple))...);
    }
    
    template <typename Tuple>
    SomeClass make_SomeClass(Tuple&& tuple)
    {
        return help_make_SomeClass(std::forward<Tuple>(tuple),
            std::make_index_sequence<std::tuple_size<Tuple>::value>());
    }
    

    std::index_sequencestd::make_index_sequence 将使用 C++1y。如果找不到定义它们的标头,可以使用这些:

    template <std::size_t... Inds>
    struct index_sequence {
        static constexpr std::size_t size()
        { return sizeof...(Inds); }
    };
    
    template <std::size_t N, std::size_t... Inds>
    struct help_index_seq {
        typedef typename help_index_seq<N-1, N-1, Inds...>::type type;
    };
    
    template <std::size_t... Inds>
    struct help_index_seq<0, Inds...> {
        typedef index_sequence<Inds...> type;
    };
    
    template <std::size_t N>
    using make_index_sequence = typename help_index_seq<N>::type;
    

    现场示例,C++11 模式:http://coliru.stacked-crooked.com/a/ed91a67c8363061b

    【讨论】:

      【解决方案4】:

      C++14 将添加对index_sequence 的标准支持:

      template<typename T, typename... Args>
      struct foo {
        tuple<Args...> args;
        T gen() { return gen_impl(std::index_sequence_for<Args...>()); }
      private:
        template <size_t... Indices>
        T gen_impl(std::index_sequence<Indices...>) { return T(std::get<Indices>(args)...); }
      };
      

      【讨论】:

        【解决方案5】:

        你需要使用indices trick,这意味着一个间接层:

        template <std::size_t... Is>
        struct indices {};
        template <std::size_t N, std::size_t... Is>
        struct build_indices
          : build_indices<N-1, N-1, Is...> {};
        template <std::size_t... Is>
        struct build_indices<0, Is...> : indices<Is...> {};
        
        template<typename T, typename... Args>
        struct foo {
          tuple<Args...> args;
          T gen() { return gen(build_indices<sizeof...(Args)>{}); }
        private:
          template<std::size_t... Is>
          T gen(indices<Is...>) { return T(get<Is>(args)...); }
        };
        

        【讨论】:

        • 我花了一些时间才发现build_indices 的第二个版本是第一个版本的特化,而不是相反。首先阅读 Andy Prowl 更详细的答案肯定会有所帮助。
        【解决方案6】:

        创建从 0 到 n-1 的索引序列:

        template<size_t... indexes>
        struct seq {};
        
        template<size_t n, size_t... indexes>
        struct make_seq: make_seq<n-1, n-1, indexes...> {};
        
        template<size_t... indexes>
        struct make_seq: make_seq<0, indexes...> {
          typedef seq<indexes...> type;
        };
        

        将它们与您的参数并行解压缩,或者在您的情况下作为get&lt;&gt; 的索引。

        目标是这样的:

        template< typename T, typename Tuple, typename Indexes >
        struct repack;
        
        template< typename... Ts, size_t... indexes >
        struct repack< tuple<Ts...>, seq<indexes...> > {
          T operator()( tuple<Ts...> const& args ) const {
            return T( get<indexes>(args)... );
          }
        };
        

        在您的gen 中使用repack,如下所示:

        T gen() {
          repack<T, tuple<Args...>, typename make_seq<sizeof...(Args)>::type> repacker;
          return repacker( args );
        }    
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-02-19
          • 2018-10-03
          • 1970-01-01
          • 1970-01-01
          • 2015-07-28
          相关资源
          最近更新 更多