Constructor arguments from tuple

2019-01-24 14:06发布

Suppose I have a template which is parametrized by a class type and a number of argument types. a set of arguments matching these types are stored in a tuple. How can one pass these to a constructor of the class type?

In almost C++11 code:

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

How can the ... in the constructor call be filled without fixing the length?

I guess I could come up with some complicated mechanism of recursive template calls which does this, but I can't believe that I'm the first to want this, so I guess there will be ready-to-use solutions to this out there, perhaps even in the standard libraries.

4条回答
你好瞎i
2楼-- · 2019-01-24 14:31

Create a sequence of indexes from 0 through 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;
};

unpack them in parallel with your args, or as the index to get<> in your case.

The goal is something like:

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)... );
  }
};

use repack in your gen like this:

T gen() {
  repack<T, tuple<Args...>, typename make_seq<sizeof...(Args)>::type> repacker;
  return repacker( args );
}    
查看更多
Juvenile、少年°
3楼-- · 2019-01-24 14:35

You'll need to use the indices trick, which means a layer of indirection:

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)...); }
};
查看更多
孤傲高冷的网名
4楼-- · 2019-01-24 14:39

You need some template meta-programming machinery to achieve that.

The easiest way to realize the argument dispatch is to exploit pack expansion on expressions which contain a packed compile-time sequence of integers. The template machinery is needed to build such a sequence (also see the remark at the end of this answer for more information on a proposal to standardize such a sequence).

Supposing to have a class (template) index_range that encapsulates a compile-time range of integers [M, N) and a class (template) index_list that encapsulates a compile-time list of integers, this is how you would use them:

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
            );
    }
};

And here is a possible implementation of index_range and index_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;

Also notice, that an interesting proposal by Jonathan Wakely exists to standardize an int_seq class template, which is something very similar to what I called index_list here.

查看更多
趁早两清
5楼-- · 2019-01-24 14:40

C++14 will add standard support for 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)...); }
};
查看更多
登录 后发表回答