You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让绑定对象专属删除器的std::unique_ptr禁止非nullptr参数的reset调用

方案1:编译期禁止非nullptr的reset调用(推荐)

核心思路是利用std::unique_ptr的特性:reset方法接受的参数类型和删除器定义的pointer别名完全兼容。我们只需要自定义一个仅允许从nullptr构造的指针包装类型作为删除器的pointer,就能在编译期直接拦截所有传入裸指针的reset调用。

修改后的完整实现代码如下:

// 自定义指针包装类型,仅允许nullptr构造,内部可访问裸指针
template<typename T>
struct SafePtr {
    T* raw_ptr = nullptr;
    // 仅允许nullptr隐式转换,外部传裸指针会编译失败
    constexpr SafePtr(std::nullptr_t) noexcept {}
private:
    // 内部构造接口,仅开放给删除器和工厂函数
    explicit SafePtr(T* p) noexcept : raw_ptr(p) {}
    friend struct MyDeleter<T>;
    template<typename U, typename... Args>
    friend my_unique_ptr<U> make_my_unique(Args&&... args);
};

template<typename T>
struct MyDeleter {
    // 定义删除器的pointer类型为自定义包装类
    using pointer = SafePtr<T>;
    size_t objectSize;

    MyDeleter() : objectSize(sizeof(T)) {}
    template<typename S>
    MyDeleter(const MyDeleter<S>& other) noexcept : objectSize(other.objectSize) {}

    void operator()(pointer p) {
        T* t = p.raw_ptr;
        if (!t) return;
        t->~T();
        coolAllocatorLibrary::deallocate(t, objectSize); 
    }
};

template<typename T>
using my_unique_ptr = std::unique_ptr<T, MyDeleter<T>>;

template<typename T, typename... Args>
my_unique_ptr<T> make_my_unique(Args&&... args) {
   T* ptr = static_cast<T*>(coolAllocatorLibrary::allocate(sizeof(T)));
   try {
      new (ptr) T(std::forward<Args>(args)...);
   } catch(...) {
      coolAllocatorLibrary::deallocate(ptr, sizeof(T));
      throw;
   }
   // 用私有构造的SafePtr构造unique_ptr
   return my_unique_ptr<T>(SafePtr<T>(ptr));
}

此时用户调用ptr.reset(new Base())时,因为Base*无法隐式转换为SafePtr<Base>,会直接编译报错,只有ptr.reset(nullptr)可以正常执行,完全符合需求。这个方案仅修改删除器和工厂函数代码,无需封装整个std::unique_ptr,实现成本极低。

方案2:运行期校验(兼容老代码场景)

如果因为历史代码限制无法修改删除器的pointer类型,可以在删除器的调用逻辑中增加运行时校验,配合调试期的断言快速发现误用:

template<typename T>
struct MyDeleter {
    size_t objectSize;
    // 存储创建时的类型信息用于校验
    const std::type_info* typeInfo = &typeid(T);

    MyDeleter() : objectSize(sizeof(T)) {}
    template<typename S>
    MyDeleter(const MyDeleter<S>& other) noexcept : 
        objectSize(other.objectSize), typeInfo(other.typeInfo) {}

    void operator()(T* t) {
        if (!t) return;
        // 调试期校验类型匹配,若reset传入了不同类型的指针会触发断言
        assert(typeid(*t) == *typeInfo && "reset传入的对象类型与删除器不匹配");
        t->~T();
        coolAllocatorLibrary::deallocate(t, objectSize); 
    }
};

这个方案无法在编译期拦截错误,但可以在调试阶段第一时间发现误用,避免线上问题。

你提到的类PMR分配器场景的std::unique_ptr原生支持,目前C++标准委员会已有相关提案在讨论,在标准落地前,上述自定义删除器改造方案已经可以覆盖绝大多数场景的需求。


内容的提问来源于stack exchange,提问作者gexicide

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 12:06:03