【问题标题】:Invoke function with each argument使用每个参数调用函数
【发布时间】:2021-04-06 17:26:06
【问题描述】:

我想调用一个函数,每个参数都作为参数包传递。 我已经写了一个简单的模板函数:

template<class Function, class... Args>
auto for_each_arg(Function f, Args&&... args) {
    (f(std::forward<Args>(args)), ...);
}

当我将它与仅采用一个参数的函数一起使用时,它工作正常:

void foo(int a) { }
for_each_arg(foo, 1);

但是当我尝试调用一个带有两个或更多参数的函数时:

void bar(int a, int b) { }
for_each_arg(bar, 1, 2);

它给出了一个错误too few arguments for call。我该如何解决?可以在 C++17 中创建解决方案。

【问题讨论】:

  • 递归可以吗?或者您是否需要模板折叠解决方案?
  • 任何解决方案都可以,不必折叠。

标签: c++ templates c++17 variadic-templates variadic-functions


【解决方案1】:
void bar(int a, int b) { }
for_each_arg(
  [](auto&&tup){
    return std::apply(bar, decltype(tup)(tup));
  }
)(std::make_tuple(1, 2));

这里调用者将参数捆绑到元组中,然后我们将它们解包到调用中。这使得哪些参数的包装变得清晰。

我们可以将 apply 调用移到 foreach 函数中。理论上,我们检测目标是否接受 1 个参数,如果不将参数解包为元组,但我建议不要这样做。像这样的混乱操作很多事情都会出错。

apply_foreach(f, tuples...)

是一个干净且易于指定的函数。

【讨论】:

  • 谢谢,它工作正常。我稍微改变了我的问题,现在正确的函数调用是for_each_arg([](auto&amp;&amp; tup) { return std::apply(bar, decltype(tup)(tup)); }, std::make_tuple(1, 2)); - 如果你能在你的答案中更正它,请。很抱歉造成混淆。
  • 同意自动检测数量只会令人困惑。一方面,如果函子类型是具有重载或可变参数operator() 的类,则没有明显正确的行为。
  • @achep plus,将 airity 从 2 修改为 3,一些可怜的草皮用 6 来称呼它真的很惊讶。
  • @Yakk-AdamNevraumont 我还有一个问题:是否需要像我的for_each_arg() 一样创建函数?我正在查看您的解决方案,似乎std::apply() 就足够了,我们不需要使用任何附加功能(如for_each_arg())来包装它。
  • @mucha 不,它只是让调用站点不那么混乱。
【解决方案2】:

古老的递归方法很简单

#include <iostream>

template <typename F>
void invoke_two_args (F)
 { }

template <typename F, typename A0, typename A1, typename ... Args>
void invoke_two_args (F f, A0 && a0, A1 && a1, Args && ... args)
 {
   f(std::forward<A0>(a0), std::forward<A1>(a1));

   invoke_two_args(f, std::forward<Args>(args)...);
 }

void bar(int a, int b)
 { std::cout << "bar: " << a << ' ' << b << '\n'; }

int main ()
 {
   // compile
   invoke_two_args(bar, 2, 3, 5, 7, 11, 13);

   // compilation error: wrong number of arguments
   // invoke_two_args(bar, 2, 3, 5, 7, 11, 13, 17);
 }

仅当参数列表不太长时,这才是一个很好的解决方案。

【讨论】:

  • 感谢您的解决方案,但 Yakk - Adam 的解决方案更短,似乎更容易理解。
  • @Michal - Yakk 的解决方案也更加灵活(不仅是两个参数,而且通常是 n 个参数)。
【解决方案3】:

你可以这样使用:

template <typename F, typename... Args>
auto curry(F&& f, Args&&... args) {
  if constexpr (std::is_invocable_v<F, Args...>) {
    return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
  } else {
    return [&](auto&&... more_args) {
      return curry(std::forward<F>(f), std::forward<Args>(args)..., more_args...);
    };
  }
}

int foo(int a, int b) { return a + b; }

// usage
auto func = curry(foo);
func(1)(2);

【讨论】:

  • 我认为你对 func(1, 2, 3, 4); 有问题,它需要 foo(1,2); foo(3, 4);
  • foo 只有两个参数,当你调用 foo(1, 2, 3, 4) 时你会期望什么
  • OP 显示了一个for_each_arg,它允许为每个参数调用f(数量为1)(即使OP 的示例仅使用一个:/)。 arity 2 的函数应该两个两个取元素。我认为您误解了 OP 的问题(IMO 不是很清楚)。
猜你喜欢
  • 1970-01-01
  • 2018-12-28
  • 2018-01-02
  • 1970-01-01
  • 2012-08-15
  • 2013-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多