将带有unique_ptr参数的函数绑定bind到std::function<void()>上的问题

最近在写代码的时候碰到了如下的问题,下面的例子可能看上去比较蠢,但足以说明我想表达的问题,我们有一个函数回调,用类型std::function<void>来表示,此时我们有一个类成员接收move-only的参数,比如unique_ptr,此时使用bind绑定出现错误

class Foo{
public:
	void dothings(unique_ptr<int> uptr)
	{
		cout << "dothings" << endl;
	}
};

int main()
{
	auto uptr = std::make_unique<int>(1);
	Foo foo;
	std::function<void> f = std::bind(&Foo::dothings, std::move(uptr));	
	f();
}

这里导致失败的主要原因是std::function 需要一个 CopyConstructible Callable target,这里我们使用的std::bind虽然能隐式转换为一个std::function对象,然而由于bind参数出现了move only的对象,因此std::bind返回的functor也是move-only,不满足std::function所需要的CopyConstructible 条件,因此虽然std::bind这里不会触发copy操作,但是在其它某个地方,准确的说就是std::function在构造的时候,会拷贝形参的functor,当然也会拷贝其底层保存的参数,从而触发了对unique_ptr的拷贝,这是不允许的,编译出错。

解法方案:
使用 lambda 代替bind,并用引用捕获unique_ptr

1 auto func = [this = &foo, &uptr]()
2 {
3 	this->dothings(std::move(uptr));
4 };
5 func();  // Synchronous call, fine
5 otherthread->runAtThatThread(func); // Asynchronous call, may be problematic

但是特别注意,这种方式实现时uptr的生命周期问题,2,3,4行代码并没有将uptr的对象生命周期交给func管理,也就是说如果在func调用之前uptr已经被释放,此时std::move(uptr)会对已经析构的uptr进行操作,非常危险

如果你确实需要使用unique_ptr,并想要将对象的生命周期交给lambda管理,对不起,目前没有很好的解决方法,也许你只能退而求其次,使用shared_ptr,它支持std::function所要求的CopyConstructible ,配合std::bind也能很好的工作

参考Stack Overflow上的讨论【传送】 ttps://stackoverflow.com/questions/20268482/binding-functions-with-unique-ptr-arguments-to-stdfunctionvoid

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这个错误通常是因为尝试将一个类型为`std::shared_ptr<rclcpp::Subscription<const std::shared_ptr<std_msgs::msg::String_<std::allocator<void>>> &, std::allocator<void>, rclcpp::message_memory_strategy::MessageMemoryStrategy<const std::shared_ptr<std_msgs::msg::String_<std::allocator<void>>> &, std::allocator<void>>>>`的变量赋值给一个类型为`std::shared_ptr<rclcpp::Subscription<std_msgs::msg::String, std::allocator<void>, rclcpp::message_memory_strategy::MessageMemoryStrategy<std_msgs::msg::String, std::allocator<void>>>>`的变量,这两个类型虽然都是指向`rclcpp::Subscription`的`shared_ptr`,但是模板参数不同,无法直接赋值。 解决方法是将两个类型匹配,可以通过使用`std::static_pointer_cast`将其中一个类型转换成另一个类型,例如: ``` std::shared_ptr<rclcpp::Subscription<std_msgs::msg::String, std::allocator<void>, rclcpp::message_memory_strategy::MessageMemoryStrategy<std_msgs::msg::String, std::allocator<void>>>> sub; std::shared_ptr<rclcpp::Subscription<const std::shared_ptr<std_msgs::msg::String_<std::allocator<void>>> &, std::allocator<void>, rclcpp::message_memory_strategy::MessageMemoryStrategy<const std::shared_ptr<std_msgs::msg::String_<std::allocator<void>>> &, std::allocator<void>>>> sub_const; // 将 sub_const 转换成 sub 的类型 sub = std::static_pointer_cast<rclcpp::Subscription<std_msgs::msg::String, std::allocator<void>, rclcpp::message_memory_strategy::MessageMemoryStrategy<std_msgs::msg::String, std::allocator<void>>>>(sub_const); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值