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

如何实现编译期固定最大引用计数的共享指针?

编译期限制最大引用计数的共享指针实现方案

要实现编译期固定最大引用计数的共享指针,不能依赖std::shared_ptr的运行期计数逻辑,得靠C++模板类型系统在编译阶段卡死拷贝次数——用模板参数指定最大引用数,通过拷贝构造/赋值的重载约束,让超过限额的拷贝直接触发编译错误。

实现思路

核心是用编译期模板参数跟踪剩余可拷贝次数:

  • 用外层模板参数MaxRef指定最大引用总数(比如你的场景是2)
  • 内部嵌套一个带非类型模板参数Remaining的impl类,标记当前实例还能被拷贝多少次
  • 只有当Remaining > 0时,才允许拷贝构造/赋值;拷贝后新实例的Remaining自动减1,原实例的可用次数同步消耗
  • 用C20的requires表达式(或C17及更早的static_assert+特化)做编译期检查,不满足条件直接报错

完整实现代码

#include <memory>
#include <utility>

template <size_t MaxRef, typename T>
class limited_shared_ptr {
private:
    template <size_t Remaining>
    class impl {
    public:
        std::shared_ptr<T> ptr;

        // 从原始指针/unique_ptr构造
        explicit impl(T* raw_ptr) : ptr(raw_ptr) {}
        explicit impl(std::unique_ptr<T>&& u_ptr) : ptr(std::move(u_ptr)) {}

        // 移动构造/赋值:不受拷贝次数限制
        impl(impl&&) noexcept = default;
        impl& operator=(impl&&) noexcept = default;

        // 拷贝构造:仅当还有剩余拷贝名额时允许
        impl(const impl& other) requires (Remaining > 0) : ptr(other.ptr) {}

        // 拷贝赋值:仅当还有剩余拷贝名额时允许
        impl& operator=(const impl& other) requires (Remaining > 0) {
            ptr = other.ptr;
            return *this;
        }

        // 生成新实例并消耗一个拷贝名额
        impl<Remaining - 1> copy() const requires (Remaining > 0) {
            return impl<Remaining - 1>(ptr);
        }

        // 重载常用操作符
        T& operator*() const { return *ptr; }
        T* operator->() const { return ptr.get(); }
    };

    impl<MaxRef - 1> m_impl; // 自身占一个引用,剩余可拷贝次数为MaxRef-1

public:
    // 构造函数
    explicit limited_shared_ptr(T* raw_ptr) : m_impl(raw_ptr) {}
    explicit limited_shared_ptr(std::unique_ptr<T>&& u_ptr) : m_impl(std::move(u_ptr)) {}

    // 拷贝构造:自动消耗一个名额
    limited_shared_ptr(const limited_shared_ptr& other) : m_impl(other.m_impl.copy()) {}

    // 拷贝赋值:自动消耗一个名额
    limited_shared_ptr& operator=(const limited_shared_ptr& other) {
        m_impl = other.m_impl.copy();
        return *this;
    }

    // 移动构造/赋值:默认实现
    limited_shared_ptr(limited_shared_ptr&&) noexcept = default;
    limited_shared_ptr& operator=(limited_shared_ptr&&) noexcept = default;

    // 暴露常用接口
    T& operator*() const { return *m_impl; }
    T* operator->() const { return m_impl.operator->(); }
    T* get() const { return m_impl.ptr.get(); }
};

// 辅助创建函数,类似std::make_shared
template <size_t MaxRef, typename T, typename... Args>
limited_shared_ptr<MaxRef, T> make_limited_shared(Args&&... args) {
    return limited_shared_ptr<MaxRef, T>(new T(std::forward<Args>(args)...));
}

// 测试用例
int main() {
    const auto p    = make_limited_shared<2, double>(2159); // 正常编译
    const auto q    = p; // 正常编译
    // const auto err1 = p; // 编译错误:剩余拷贝次数为0,不满足requires约束
    // const auto err2 = q; // 编译错误:q的剩余拷贝次数为0,无法拷贝
    return 0;
}

针对你的队列场景的适配

把你的队列类型替换掉代码里的double,用limited_shared_ptr<2, YourQueueType>来封装队列实例:

  • 写入线程持有第一个实例,读线程持有通过拷贝得到的第二个实例
  • 任何尝试创建第三个实例的操作都会直接触发编译错误,从根源上保证不会出现超过两个线程同时持有队列的情况,完美匹配你单读单写的需求

兼容性说明

如果你的项目还在使用C++17或更早版本,可以把requires表达式换成static_assert结合模板特化的方式:

  • 为Remaining=0的impl类显式禁用拷贝构造和赋值
  • 在拷贝构造函数里加入static_assert(Remaining > 0, "Exceeded maximum reference count")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 19:03:24