带捕获的Lambda作为std::unique_ptr删除器调用testClose报错如何解决?
我需要打开文件并执行相关操作,testOpen 和 testClose 是C库test中test_open、test_close的包装器,操作出错时会抛出异常以便排查问题。我尝试使用std::unique_ptr的自定义删除器来执行文件关闭操作,需将函数参数std::string const& path传递给testClose(test*, std::string&)。但当我使用带path捕获的Lambda作为删除器时,出现编译错误:
error: no matching function for call to 'std::unique_ptr<test, void()(test)>::unique_ptr(test*, testOpen(std::string const&)::<lambda(test*)>)'
而直接调用test_close的无捕获Lambda则可正常运行。请问如何实现使用testClose包装器作为删除器的功能?
核心问题是带捕获的Lambda无法隐式转换为普通函数指针,而你声明std::unique_ptr时指定的删除器类型是void(*)(test*)(函数指针),这就导致类型不匹配。下面是两种可行的解决思路:
方法一:让unique_ptr自动推导删除器类型(推荐)
不要显式指定删除器的函数指针类型,用auto推导Lambda的实际类型,这样带捕获的Lambda就能正常作为删除器:
auto open_file(const std::string& path) { test* handle = testOpen(path); // 捕获path的Lambda作为删除器,auto会推导正确的删除器类型 return std::unique_ptr<test, decltype([path](test* t) { testClose(t, const_cast<std::string&>(path)); })>( handle, [path](test* t) { testClose(t, const_cast<std::string&>(path)); } ); }
注意:这里用const_cast是因为testClose的第二个参数是std::string&,但我们捕获的是const&。如果testClose不需要修改path,建议把它的参数改成const std::string&,避免不必要的类型转换。
方法二:使用std::function作为删除器类型
显式将unique_ptr的删除器类型指定为std::function<void(test*)>,它可以容纳带捕获的Lambda:
std::unique_ptr<test, std::function<void(test*)>> open_file(const std::string& path) { test* handle = testOpen(path); return std::unique_ptr<test, std::function<void(test*)>>( handle, [path](test* t) { testClose(t, const_cast<std::string&>(path)); } ); }
这种方法的缺点是std::function会带来轻微的性能开销,对性能要求极高的场景优先选第一种方法。
额外优化建议
如果testClose本身不需要path参数,只是当前包装器设计的要求,建议重新调整testClose的接口,让它只接收test*参数(直接调用底层的test_close),这样就能用无捕获Lambda,彻底避免捕获带来的类型问题。
内容的提问来源于stack exchange,提问作者Yash Sakariya

