在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);
}

doTemplate接受一个函数作为模板参数,而doParam接受一个函数作为函数指针,它们似乎都给出相同的结果。

使用每种方法之间的权衡是什么?

最佳答案

基于模板的版本允许编译器内联调用,因为该函数的地址在编译时是已知的。显然,缺点是必须在编译时知道函数的地址(因为您将其用作模板参数),有时这是不可能的。

这使我们进入第二种情况,其中只能在运行时确定函数指针,从而使编译器无法执行内联,但是为您提供了在运行时确定要调用的函数的灵活性:

bool runtimeBooleanExpr = /* ... */;
doParam(0, runtimeBooleanExpr ? add1 : add2);

但是请注意,还有第三种方法:
template<typename F>
void doParam(int i, F f){
    std::cout << "Do Param: " << f(i) << "\n";
}

这为您提供了更大的灵活性,并且仍然具有在编译时知道将调用什么函数的优点:
doParam(0, add1);
doParam(0, add2);

并且它还允许传递任何可调用对象而不是函数指针:
doParam(0, my_functor());

int fortyTwo = 42;
doParam(0, [=] (int i) { return i + fortyTwo; /* or whatever... */ }

为了完整起见,还有第四种方法,使用std::function:
void doParam(int x, std::function<int(int)> f);

通用性相同(因为您可以传递任何可调用对象),但也可以让您在运行时确定可调用对象-最有可能导致性能下降,因为(再一次)内联对于编译器而言是不可能的。

有关最后两个选项的进一步讨论,请参见this Q&A on StackOverflow

07-24 09:50
查看更多