2

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

我如何编写自己的函数来接受这些参数?

举个简单的例子,假设我的课程是:

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

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

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

其中声明“功能”是我不确定的,除其他外。术语“funcall”来自 Lisp,(funcall f a b c)仅适用f于参数 ab 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”问题基本相同,但不需要实际调用传递的函数对象。

4

2 回答 2

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)>,允许您直接使用具有不可知类型funcall的 lambda 和bind/mem_fn表达式进行调用。直接传递这些允许有效的内联机会,而创建std::function对象则比较贵。

于 2014-05-17T13:43:37.037 回答
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++ 函数只是指针。

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

于 2014-05-19T04:10:58.597 回答