c++ - 从函数参数构建模板?

标签 c++ c++14 variadic-templates template-meta-programming

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]
}

假设:

  • Foos 中的所有类型都是可调用的
  • Foos 中的所有类型都是明确的
  • Foos 中的任何类型的元数都可以为 0
  • ArgsFoos
  • 使用的所有参数类型的串联

仅使用 Foos不使用 Args 是否可以做到这一点?即使我确实明确列出了它们,我实际上也不确定该怎么做。

最佳答案

我试图组合一个非递归实例化版本,但它涉及一些当前不存在的实用程序。

拆分通话

假设我们有 F这需要 2 int s 和 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_sequence

假设我们的函数参数是 [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]并将它们压缩以获得我们想要的范围。

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

实用程序

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

标准::应用

我们需要的部分实际上是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))...);
}

函数参数

用于确定函数的元数。

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

制作索引范围

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

切片

切片 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>());
}

部分总和

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

Full solution on Ideone

奖金

我将分享这一部分,因为我为了好玩而进一步玩这个。我不会讲太多细节,因为这不是要求的内容。

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

Full solution on Ideone

关于c++ - 从函数参数构建模板?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32288778/

相关文章:

c++ - 未处理的异常...位置访问冲突..在 C++ 中运行 kinect 应用程序时

c++ - 是否有一种技术可以让匿名结构的命名实例引用封闭类中的函数?

java - JNI 'problematic frame' 导致 JVM 崩溃

c++ - 还有什么办法可以实现 "templated function pointers"?

c++ - ClangOnWin 是一个可靠的发行版吗?

c++ - 实例化类模板的可变成员函数模板

c++ - C++11 中可变参数模板的函数组合

c++ - 交换可变参数模板中的两个参数

c++ - 如何在另一个命名空间中键入定义一个命名空间的枚举并使用 ist c++

c++ - `std::bind()` 标准库算法如何实现?