Pet*_*ter 9 c++ templates function
在C++中,有两种方法可以将函数传递给另一个看似等效的函数.
#include <iostream>
int add1(int i){ return i+1; }
int add2(int i){ return i+2; }
template <int (*T)(int) >
void doTemplate(int i){
std::cout << "Do Template: " << T(i) << "\n";
}
void doParam(int i, int (*f)(int)){
std::cout << "Do Param: " << f(i) << "\n";
}
int main(){
doTemplate<add1>(0);
doTemplate<add2>(0);
doParam(0, add1);
doParam(0, add2);
}
Run Code Online (Sandbox Code Playgroud)
doTemplate接受一个函数作为模板参数,而doParam接受它作为函数指针,它们似乎都给出了相同的结果.
使用每种方法之间的权衡是什么?
And*_*owl 11
基于模板的版本允许编译器内联调用,因为函数的地址在编译时是已知的.很明显,缺点是函数的地址有在编译时是已知的(因为你正在使用它作为一个模板参数),有时这是不可能的.
这将我们带到第二种情况,其中函数指针只能在运行时确定,因此编译器无法执行内联,但可以灵活地在运行时确定要调用的函数:
bool runtimeBooleanExpr = /* ... */;
doParam(0, runtimeBooleanExpr ? add1 : add2);
Run Code Online (Sandbox Code Playgroud)
但请注意,还有第三种方法:
template<typename F>
void doParam(int i, F f){
std::cout << "Do Param: " << f(i) << "\n";
}
Run Code Online (Sandbox Code Playgroud)
这为您提供了更大的灵活性,并且仍然具有在编译时了解将要调用的函数的优势:
doParam(0, add1);
doParam(0, add2);
Run Code Online (Sandbox Code Playgroud)
它还允许传递任何可调用对象而不是函数指针:
doParam(0, my_functor());
int fortyTwo = 42;
doParam(0, [=] (int i) { return i + fortyTwo; /* or whatever... */ }
Run Code Online (Sandbox Code Playgroud)
为了完整起见,还有第四种方法,使用std::function:
void doParam(int x, std::function<int(int)> f);
Run Code Online (Sandbox Code Playgroud)
它具有相同的通用性(因为你可以传递任何可调用对象),但也允许你在运行时确定可调用对象 - 最有可能带来性能损失,因为(再次)内联对编译器来说变得不可能.
有关最后两个选项的进一步讨论,请参阅StackOverflow上的此问答.
模板参数
函数指针
if/ switch为参数的特殊值做特殊的东西,这很脆弱何时使用哪个版本:如果您需要速度和大量自定义,请使用模板.如果您在运行时需要灵活性,而不是在实现中,请使用函数指针.
正如@AndyProwl所指出的那样:如果你有一个C++ 11编译器,函数指针将被推广到可调用对象,例如std::functionlambda表达式.这开辟了一整套新的蠕虫(在很好的意义上).