pybind11绑定带抽象类型引用参数的lambda函数时遇到非可复制类型错误
pybind11绑定带抽象类型引用参数的lambda函数时遇到非可复制类型错误
这个问题我之前也踩过坑,核心是pybind11默认的对象传递策略和抽象类的特性冲突了,咱们一步步捋清楚:
为什么会报“非可复制类型”错误?
当你在Python里调用evaluate时,C侧会创建一个内部的Foo实例,然后把它的引用IFoo&传给Python的回调函数。这时候pybind11需要把C的引用转换成Python能识别的对象,但它默认会用copy(复制)作为返回值策略——也就是尝试复制这个IFoo对象。
但IFoo是抽象类啊!抽象类根本不能实例化,自然也无法复制,所以直接触发了“type IFoo is non-copyable”的错误。
而你改成指针IFoo*时,pybind11默认会把指针包装成一个引用型的Python对象,不会尝试复制,所以就正常工作了。
怎么解决?
我们只需要告诉pybind11:这个IFoo&指向的对象是C++内部管理生命周期的,Python只需要临时用一下它的引用,不用复制。具体就是在绑定evaluate函数时,给回调参数指定reference_internal的返回值策略。
修改你的绑定代码如下:
PYBIND11_MODULE(libbindtest, module) { // 绑定抽象类IFoo(注意Python端命名为"Foo",这里可以根据需要改名,比如"IFoo"更清晰) py::class_<IFoo>(module, "Foo") .def("setValue", &IFoo::setValue); // 绑定evaluate函数,同时指定回调参数的返回值策略为reference_internal module.def("evaluate", &evaluate, py::return_value_policy::reference_internal); }
如果你想更严谨地指定参数,可以用py::arg明确标注:
module.def("evaluate", &evaluate, py::arg("callback").policy(py::return_value_policy::reference_internal));
修改完后,你的Python代码就能正常运行了,执行后会输出22+34=56,完全符合预期。
额外补充
reference_internal策略的核心是:告诉pybind11,这个对象的生命周期由C全权管理,Python拿到的只是临时引用,不能在C对象销毁后继续使用(这里Foo实例在evaluate执行完就销毁,刚好匹配这个场景)。- 你当前把C的
IFoo在Python端命名为Foo,虽然能跑,但容易和C的实现类Foo混淆,建议改成IFoo,可读性更好~
内容来源于stack exchange
相关产品推荐
相关产品推荐

