网上有很多使用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”问题基本相同,但实际上没有要求调用传递的函数对象。
答案 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 ++函数只是指针。
所以答案是你可以编写自己的函数,当你知道那些参数只是指向合适类型函数的指针时,将函数作为参数,如上所示声明。