有没有一种思想方法可以避免使用 Naked New?

Is there an ideomatic way to avoid this use of Naked New?

在我的应用程序中,我需要设置回调,其唯一职责是在单独的对象中更新与已完成事件关联的状态,以便稍后查询。但是,由于 API 的设计方式,我无法保证事件完成时另一个对象仍将拥有,因此我需要存储指向该对象的指针,并且因为回调 API 是基于 C 的,我最终存储了一个指向智能指针的原始指针,这是我见过的最丑陋的代码段*。

* 反正最近几个小时...

所以这就是我写的来完成这个:

event.setCallback(CL_COMPLETE, [](cl_event event, cl_int, void* ptr) {
    auto ptr_ptr = static_cast<std::weak_ptr<render_future::shared_state>*>(ptr);
    if(auto shared_ptr = ptr_ptr->lock()) {
        auto & shared_state = *shared_ptr;
        std::lock_guard lock{ shared_state.mutex };
        shared_state.event_state[event] = true;
    }
    delete ptr_ptr;
}, new std::weak_ptr<render_future::shared_state>(future.state));

我特别反对自己使用 new std::weak_ptr<render_future::shared_state>(future.state),这对我来说似乎是某种反模式:将裸 newdelete 与智能指针结合使用。

然而,问题是因为回调必须是函数指针,我的 lambda 表达式无法复制或引用其他对象,而在 lambda 中获取 shared_state 对象的唯一方法是传递它的指针;再一次,因为我不能保证它的生命周期没有过期,所以我需要以指向 weak_ptr 的指针的形式获取它,以便在(且仅当)对象仍然存在时可以对其进行操作。

所以最终,我的问题是:是否有一种思想方法可以将 shared_state 传递到此回调中,其中

  1. 我可以检查以确保该对象仍然存在,而
  2. 还要消除我对裸 newdelete 调用的使用吗?

在 lambda 中,只需使用 unique_ptr:

就可以实现明显的改进
auto callback = [](cl_event event, cl_int, void* ptr) {
    std::unique_ptr<std::weak_ptr<render_future::shared_state>> ptr_ptr{ static_cast<std::weak_ptr<render_future::shared_state>*>(ptr)};
    if(auto shared_ptr = ptr_ptr->lock()) {
        std::lock_guard lock{ shared_state->mutex };
        shared_state->event_state[event] = true;
    }
}

在事物的创造方面,您可以使用 std::unique_ptr<>::release()

auto ptr = std::make_unique<std::weak_ptr<render_future::shared_state>>(future.state);

event.setCallback(CL_COMPLETE, callback, ptr.release());

但是由于您正在使用无捕获 lambda 调用 C 函数,因此这里并没有要防止的意外异常,所以这是否是对您当前正在做的事情的真正改进还有待商榷。

您可以将智能指针存储到映射中的对象,以非拥有指针作为键。回调可以从地图中删除指针,从而释放所有权。地图必须比回调长寿。如果需要,您可以求助于静态存储。

使用唯一所有权,您可以直接将对象本身存储在地图中。

在唯一所有权的情况下,用于跟踪分配的数据结构的额外复杂性和开销是否值得摆脱显式新建和删除值得商榷。对于共享所有权,这可能是更好的选择。