如何正确传递非静态成员函数给Solver?规避模板与虚继承开销
解决方案
首先,你现有代码的核心问题是:非静态成员函数必须绑定所属对象实例才能被调用,直接取&obj.eval得到的是成员函数指针(类型为void (Problem1::*)(double&, double&)),无法直接赋值给std::function<void(double&, double&)>,因为后者不包含对象上下文。
结合你的场景限制(不使用模板、避免虚函数性能开销),以下两种方案可以解决问题:
方案一:使用std::bind+std::function(简单易用,性能足够多数场景)
std::bind可以把对象实例和成员函数绑定,生成一个符合std::function签名的可调用对象,性能开销极小,完全能应对10000次循环的场景。
修改后的代码如下:
#include<iostream> #include<functional> struct Solver{ // 直接存储std::function对象,而非指针(避免空指针风险) std::function<void(double&, double&)> f; double solve(){ double y,x=1; for(long int i=0;i<10000;++i){ f(y,x); f(x,y); } return y; } }; struct Problem1{ double p; void eval(double& y, double& x){ y = x*x - p; } }; int main(){ Problem1 obj; obj.p = 2.0; // 给参数赋值,避免未定义行为 Solver solver; // 用std::bind绑定对象实例和成员函数 solver.f = std::bind(&Problem1::eval, &obj, std::placeholders::_1, std::placeholders::_2); std::cout << solver.solve() << std::endl; return 0; }
方案二:函数指针+上下文指针(极致性能,接近直接调用)
如果追求极致性能,完全避免std::function的微小开销,可以用C风格的"函数指针+void*上下文"方案,这种方式和直接调用成员函数的性能几乎一致,没有虚函数的间接跳转开销。
代码示例:
#include<iostream> // 定义统一的函数指针类型,包含上下文指针参数 using EvalFunc = void (*)(void*, double&, double&); struct Solver{ EvalFunc f; void* ctx; // 存储对象实例的指针作为上下文 double solve(){ double y,x=1; for(long int i=0;i<10000;++i){ f(ctx, y, x); f(ctx, x, y); } return y; } }; struct Problem1{ double p; void eval(double& y, double& x){ y = x*x - p; } }; // 包装函数:把Problem1的eval适配成EvalFunc类型 void problem1_eval_wrapper(void* ctx, double& y, double& x){ // 将上下文指针转换为Problem1实例指针,然后调用成员函数 static_cast<Problem1*>(ctx)->eval(y, x); } int main(){ Problem1 obj; obj.p = 2.0; Solver solver; solver.f = problem1_eval_wrapper; solver.ctx = &obj; std::cout << solver.solve() << std::endl; return 0; }
性能说明
- 虚函数的开销主要是每次调用的间接跳转(CPU分支预测可能会抵消部分开销),但如果你的循环次数远超10000次,或者对延迟要求极高,虚函数的开销可能会显现。
std::function的调用通常比虚函数多1次间接跳转,但在多数场景下可以忽略不计;方案二的函数指针方式则和直接调用成员函数的性能几乎无差异。
内容的提问来源于stack exchange,提问作者user22056011
相关产品推荐
相关产品推荐

