C ++中的funcall:声明将函数作为参数的函数

时间:2014-05-17 13:39:54

标签: c++ c++11 lambda function-object

网上有很多使用STL传递函数或函数对象作为参数的例子,例如std :: count。

如何编写自己的函数来获取这些参数?

举个简单的例子,我的班级是:

struct Foo{
 int val=0;
 int methodinc()const{return val+1};
 }

我想定义一个函数funcall,如:

int funcall (foo arg, function f) 
  {return f(arg); }

其中,声明“功能”是我不确定的。术语“funcall”来自Lisp,其中(funcall f a b c)仅将f应用于参数a b c。

然后这样的事情应该有效:

Foo ff;

funcall(ff,Foo::methodinc); // should return 1
funcall(ff, [](Foo x) {return x.val+1;}) // should return 1

有哪些简单的方法可以实现这一目标?

我将这些作为调试助手编写,“funcall”将用作我自己的实现的一部分,就像我自己的数据结构的count,remove-if,transform和其他类似的带有函数参数的STL函数的类比。但我不想编写复杂的模板表达式来定义我的代码。


这个问题的初步答案表明,声明和使用函数参数的整个概念有点模糊,至少对我而言。也许在解决funcall之前,更简单的任务可能只是将函数参数传递给另一个函数,而不是使用它。例如,在C ++中,要计算向量v,我必须编写

std::count(v.begin, v.end(), [](int j){return j>3})

如何编写一个始终对整个向量进行计数的计数,以便:

 mycount(v,[](int j){return j>3})

和上面一样吗?这个“mycount”可以用于成员函数指针而不是lambdas吗?

这个问题与“funcall”问题基本相同,但实际上没有要求调用传递的函数对象。

2 个答案:

答案 0 :(得分:3)

通常,函数模板最适合这种灵活性需求:

template <typename F, typename T>
auto funcall(T && t, F f) -> decltype(f(std::forward<T>(t))
{
    return f(std::forward<T>(t));
}

您可以使用decltype特征代替尾随返回类型和result_of

template <typename F, typename T>
typename std::result_of<F(T&&)>::type funcall(T && t, F f)
{
    return f(std::forward<T>(t));
}

或者,在C ++ 14中,您可以说decltype(auto) funcall(T && t, F f)没有尾随返回类型,并且它会自动推断出来。

使F推导出模板参数而非固定类型(例如std::function<R(T)>)的主要原因是允许您直接使用lambdas和funcall调用bind / mem_fn表达式,具有不可知的类型。直接传递这些表达式可以实现有效的内联机会,而相比之下,创建std::function对象相当昂贵。

答案 1 :(得分:1)

C ++是一种非常强大而复杂的语言。在其中,您可以在Lisp中执行任何操作,包括自己实现Lisp。问题是,要实现这一目标,你必须学习很多关于语言及其能做的事情。遗憾的是,将函数用作对象是C ++中最复杂的部分之一。

有多种方法可以解决您的问题。 @Kerrek答案很棒,但显然超出了你的准备范围。编辑中提供的代码用于lambda,这不一定会使事情变得更简单。

从本质上讲,C ++中的函数对象只是指针。它们看起来像这样。

typedef int (*func)(int a, char b);

int f(int aa, char bb) {
  return aa + bb;
}

int main(void) {
  func fv = f;
  int ret = fv(10, ' ');
  printf ("ret=%d", ret);
  return 0;
}

这里func是表示函数调用的类型,f是实际函数,fv是函数调用变量。

从这个结构中建立了其他一切。使用模板,编译器可以进行类型匹配,使用lambdas可以避免使用nmes。在这一切之下,C / C ++函数只是指针。

所以答案是你可以编写自己的函数,当你知道那些参数只是指向合适类型函数的指针时,将函数作为参数,如上所示声明。