从函数的参数构建模板?

Bri*_*uez 4 c++ template-meta-programming variadic-templates c++14

template<class... Foos> // N = sizeof...(Foos)
template<typename... Args> // M = sizeof...(Args)
void split_and_call(Args&&... args)
{
    // Using Python notation here...
    Foos[0](*args[:a]);  // a = arity of Foos[0]
    Foos[1](*args[a:b]); // b-a = arity of Foos[1]
    ...
    Foos[N-1](*args[z:M]); // M-z = arity of Foos[N-1]
}
Run Code Online (Sandbox Code Playgroud)

假设:

  • 所有类型Foos都可以调用
  • 所有类型Foos都是明确的
  • 任何类型都Foos可能具有0的arity
  • Args 是所用的所有参数类型的串联 Foos

这可以在公开Foos 公平的情况下完成Args吗?即使我明确地列出了它们,我实际上也不确定该怎么做.

mpa*_*ark 7

我试图将非递归实例化版本放在一起,但它涉及一些当前不存在的实用程序.

split_and_call

假设我们F需要2 int秒,并且G需要1 int和参数1, 2, 3.

鉴于F,G,tuple(1, 2, 3),index_sequence<0, 1>,index_sequence<2>,我们要调用apply_impl(F{}, tuple(1, 2, 3), index_sequence<0, 1>{})apply_impl(G{}, tuple(1, 2, 3), index_sequence<2>{}).

扩展F,G简单Fns{}...,并使参数的元组也很简单std::forward_as_tuple(std::forward<Args>(args)...).我们留下来构建index_sequences.

假设我们的函数是arities [2, 1, 3],我们首先得到它的部分和,并在前面加上0:[0, 2, 3, 6].我们希望该指数范围是:[0, 2),[2, 3),[3, 6).

我们拆分[0, 2, 3, 6]is = [0, 2, 3],并js = [2, 3, 6]和zip他们得到我们想要的范围.

template <typename... Fns, typename Args, std::size_t... Is, std::size_t... Js>
void split_and_call_impl(Args &&args,
                         std::index_sequence<Is...>,
                         std::index_sequence<Js...>) {
  int dummy[] = {
      (apply_impl(Fns{}, std::forward<Args>(args), make_index_range<Is, Js>{}),
       0)...};
  (void)dummy;
}

template <typename... Fns, typename... Args>
void split_and_call(Args &&... args) {
  auto partial_sums = partial_sum_t<0, function_arity<Fns>{}...>{};
  auto is = slice<0, sizeof...(Fns)>(partial_sums);
  auto js = slice<1, sizeof...(Fns) + 1>(partial_sums);
  split_and_call_impl<Fns...>(
      std::forward_as_tuple(std::forward<Args>(args)...), is, js);
}
Run Code Online (Sandbox Code Playgroud)

公用事业

  • std :: apply(C++ 17)
  • function_arity
  • make_index_range
  • 切片
  • partial_sum

的std ::申请

我们需要的apply_impl部分实际上是部分.

template <typename Fn, typename Tuple, size_t... Is>
decltype(auto) apply_impl(Fn &&fn, Tuple &&tuple, std::index_sequence<Is...>) {
  return std::forward<Fn>(fn)(std::get<Is>(std::forward<Tuple>(tuple))...);
}
Run Code Online (Sandbox Code Playgroud)

function_arity

用于确定函数的arity.

template <typename F>
struct function_arity;

template <typename R, typename... Args>
struct function_arity<R (Args...)>
    : std::integral_constant<std::size_t, sizeof...(Args)> {};

template <typename R, typename... Args>
struct function_arity<R (*)(Args...)> : function_arity<R (Args...)> {};

template <typename R, typename... Args>
struct function_arity<R (&)(Args...)> : function_arity<R (Args...)> {};

template <typename R, typename C, typename... Args>
struct function_arity<R (C::*)(Args...) const> : function_arity<R (Args...)> {};

template <typename R, typename C, typename... Args>
struct function_arity<R (C::*)(Args...)> : function_arity<R (Args...)> {};

template <typename C>
struct function_arity : function_arity<decltype(&C::operator())> {};
Run Code Online (Sandbox Code Playgroud)

make_index_range

make_index_sequence<N>构造的变体index_sequence<0, .. N>.make_index_range<B, E>构造index_sequence<B, .. E>.

template <typename T, typename U, T Begin>
struct make_integer_range_impl;

template <typename T, T... Ints, T Begin>
struct make_integer_range_impl<T, std::integer_sequence<T, Ints...>, Begin> {
  using type = std::integer_sequence<T, Begin + Ints...>;
};

template <class T, T Begin, T End>
using make_integer_range =
    typename make_integer_range_impl<T,
                                     std::make_integer_sequence<T, End - Begin>,
                                     Begin>::type;

template <std::size_t Begin, std::size_t End>
using make_index_range = make_integer_range<std::size_t, Begin, End>;
Run Code Online (Sandbox Code Playgroud)

切片

index_sequence在范围内切片[Begin, End).

例如 slice<0, 2>(index_sequence<2, 3, 4, 5>{}) == index_sequence<2, 3>

template <std::size_t... Is, std::size_t... Js>
constexpr decltype(auto) slice_impl(std::index_sequence<Is...>,
                                    std::index_sequence<Js...>) {
  using array_t = std::array<std::size_t, sizeof...(Is)>;
  return std::index_sequence<std::get<Js>(array_t{{Is...}})...>();
}

template <std::size_t Begin, std::size_t End, std::size_t... Is>
constexpr decltype(auto) slice(std::index_sequence<Is...> is) {
  return slice_impl(is, make_index_range<Begin, End>());
}
Run Code Online (Sandbox Code Playgroud)

partial_sum

功能版std::partial_sum.

例如 partial_sum<2, 3, 4> == index_sequence<2, 5, 9>

template <std::size_t... Is>
struct partial_sum;

template <std::size_t... Is>
using partial_sum_t = typename partial_sum<Is...>::type;

template <>
struct partial_sum<> { using type = std::index_sequence<>; };

template <std::size_t I, std::size_t... Is>
struct partial_sum<I, Is...> {

  template <typename Js>
  struct impl;

  template <std::size_t... Js>
  struct impl<std::index_sequence<Js...>> {
    using type = std::index_sequence<I, Js + I...>;
  };

  using type = typename impl<partial_sum_t<Is...>>::type;
};
Run Code Online (Sandbox Code Playgroud)

Ideone上的完整解决方案

奖金

我将分享这部分,因为我进一步玩这个有趣.我不会详细介绍,因为它不是被问到的.

  • 更新了语法,call(fs...)(args...);以便可以传递顶级函数.例如call(f, g)(1, 2, 3)
  • 将每个函数调用的结果作为a返回std::tuple.例如auto result = call(f, g)(1, 2, 3)

Ideone上的完整解决方案