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

从std::unique_ptr集合返回原始指针的最优实现方案

最优实现方案分析

你提出的双集合存储方案确实存在冗余且容易引发同步问题,下面是几种更合理的实现方式,按推荐优先级排序:

方案1:C++20 范围视图(推荐)

无需额外存储,通过视图动态将unique_ptr<B>转换为原始指针,完全复用原集合的元素,不存在同步风险,符合零开销原则。

#include <set>
#include <memory>
#include <ranges>

class B {};

class A
{
private:
    std::set<std::unique_ptr<B>> _Bs;
public:
    // 返回一个视图,元素为B*,直接映射原集合
    auto getBs() const {
        return _Bs | std::views::transform([](const auto& ptr) { 
            return ptr.get(); 
        });
    }

    // 示例:添加元素的接口,维护所有权
    void addB(std::unique_ptr<B> b) {
        _Bs.insert(std::move(b));
    }
};

// 使用示例
int main() {
    A a;
    a.addB(std::make_unique<B>());
    a.addB(std::make_unique<B>());

    for (B* b : a.getBs()) {
        // 直接使用原始指针,不转移所有权
    }
}

方案2:自定义迭代器适配器(兼容C++11及以上)

如果无法使用C++20,可以封装迭代器适配器,实现从unique_ptr<B>到B*的转换,同样无需额外存储。

#include <set>
#include <memory>
#include <iterator>

class B {};

// 迭代器适配器:将unique_ptr<B>迭代器转换为B*迭代器
class RawPtrIter {
public:
    using iterator_category = std::bidirectional_iterator_tag;
    using value_type = B*;
    using difference_type = std::ptrdiff_t;
    using pointer = B**;
    using reference = B*&;

    explicit RawPtrIter(std::set<std::unique_ptr<B>>::const_iterator it) : _it(it) {}

    B* operator*() const { return _it->get(); }
    B* operator->() const { return _it->get(); }

    RawPtrIter& operator++() { ++_it; return *this; }
    RawPtrIter operator++(int) { auto temp = *this; ++_it; return temp; }

    RawPtrIter& operator--() { --_it; return *this; }
    RawPtrIter operator--(int) { auto temp = *this; --_it; return temp; }

    bool operator==(const RawPtrIter& other) const { return _it == other._it; }
    bool operator!=(const RawPtrIter& other) const { return !(*this == other); }

private:
    std::set<std::unique_ptr<B>>::const_iterator _it;
};

// 范围包装类,提供begin/end接口
class RawBsRange {
public:
    explicit RawBsRange(const std::set<std::unique_ptr<B>>& bs) : _bs(bs) {}
    RawPtrIter begin() const { return RawPtrIter(_bs.begin()); }
    RawPtrIter end() const { return RawPtrIter(_bs.end()); }

private:
    const std::set<std::unique_ptr<B>>& _bs;
};

class A
{
private:
    std::set<std::unique_ptr<B>> _Bs;
public:
    RawBsRange getBs() const {
        return RawBsRange(_Bs);
    }

    void addB(std::unique_ptr<B> b) {
        _Bs.insert(std::move(b));
    }
};

// 使用示例
int main() {
    A a;
    a.addB(std::make_unique<B>());
    for (B* b : a.getBs()) {
        // 使用原始指针
    }
}

不推荐的方案:维护双集合

你最初的双集合方案存在明显缺陷:

  • 同步风险:每次添加、删除_Bs中的元素时,必须手动同步_rawBs,极易遗漏导致集合不一致。
  • 内存浪费:额外存储一份指针集合,对大型集合完全没必要。
  • 野指针风险:若_Bs中的unique_ptr被释放,_rawBs中的指针会变成野指针,除非严格同步,但同步逻辑复杂度高。

如果因接口限制必须返回std::set<B*>&,可以用缓存+失效标记的方式优化,但仍不推荐:

#include <set>
#include <memory>
#include <algorithm>

class B {};

class A
{
private:
    std::set<std::unique_ptr<B>> _Bs;
    mutable std::set<B*> _rawBs;
    mutable bool _cacheValid = false;

    void syncCache() const {
        if (!_cacheValid) {
            _rawBs.clear();
            std::transform(_Bs.begin(), _Bs.end(),
                          std::inserter(_rawBs, _rawBs.begin()),
                          [](const auto& ptr) { return ptr.get(); });
            _cacheValid = true;
        }
    }

public:
    std::set<B*>& getBs() {
        syncCache();
        return _rawBs;
    }

    void addB(std::unique_ptr<B> b) {
        _Bs.insert(std::move(b));
        _cacheValid = false; // 标记缓存失效
    }

    void removeB(B* target) {
        auto it = std::find_if(_Bs.begin(), _Bs.end(),
                              [target](const auto& ptr) { return ptr.get() == target; });
        if (it != _Bs.end()) {
            _Bs.erase(it);
            _cacheValid = false;
        }
    }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 20:20:58