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

如何使expiring_map的const迭代器禁止修改value_type的second成员?

解决const迭代器可修改mutable成员的问题

要解决你的问题,核心是让const expiring_map的迭代器返回的对象中,second成员是不可修改的const T&,同时兼容::testing::ElementsAre的要求,不用手动复制std::pair的构造函数。可以通过自定义迭代器适配器+代理对象实现,具体步骤如下:


1. 定义兼容std::pair的value_type

让你的value_type直接继承std::pair<const Key, T>,自动继承所有构造函数,同时显式将second声明为mutable,保留普通迭代器的修改权限:

template <typename Key, typename T>
struct expiring_map_value : std::pair<const Key, T> {
    // 继承std::pair的所有构造函数
    using std::pair<const Key, T>::pair;
    // 显式将second声明为mutable,覆盖std::pair的const限制
    mutable T& second = std::pair<const Key, T>::second;
};

2. 实现const迭代器的代理对象

这个代理对象包装const value_type,在operator->返回的视图中把second转为const,同时支持隐式转换为value_type,满足ElementsAre的类型断言需求:

template <typename ValueType>
struct const_value_proxy {
    const ValueType* ptr;

    explicit const_value_proxy(const ValueType* p) : ptr(p) {}

    // 代理的箭头操作返回只读视图
    struct const_view {
        const typename ValueType::first_type& first;
        const typename ValueType::second_type& second;

        explicit const_view(const ValueType& v) : first(v.first), second(v.second) {}
    };

    const_view operator->() const {
        return const_view(*ptr);
    }

    // 支持转换为value_type,适配ElementsAre的断言逻辑
    operator ValueType() const {
        return *ptr;
    }
};

3. 用boost::iterator_adaptor包装const迭代器

基于Boost的迭代器适配器,把multi_index的原生const迭代器包装成自定义const迭代器,使其返回上述代理对象:

#include <boost/iterator/iterator_adaptor.hpp>

template <typename MultiIndexConstIter>
class expiring_map_const_iterator :
    public boost::iterator_adaptor<
        expiring_map_const_iterator<MultiIndexConstIter>,
        MultiIndexConstIter,
        const expiring_map_value<typename MultiIndexConstIter::value_type::first_type,
                                typename MultiIndexConstIter::value_type::second_type>,
        boost::use_default,
        const_value_proxy<typename MultiIndexConstIter::value_type>
    >
{
public:
    using base_type = boost::iterator_adaptor<
        expiring_map_const_iterator<MultiIndexConstIter>,
        MultiIndexConstIter,
        const expiring_map_value<typename MultiIndexConstIter::value_type::first_type,
                                typename MultiIndexConstIter::value_type::second_type>,
        boost::use_default,
        const_value_proxy<typename MultiIndexConstIter::value_type>
    >;

    expiring_map_const_iterator() = default;
    explicit expiring_map_const_iterator(MultiIndexConstIter it) : base_type(it) {}

private:
    friend class boost::iterator_core_access;

    const_value_proxy<typename MultiIndexConstIter::value_type> dereference() const {
        return const_value_proxy<typename MultiIndexConstIter::value_type>(&(*this->base()));
    }
};

4. 在expiring_map中集成自定义迭代器

修改expiring_map类,定义普通迭代器和自定义const迭代器,并实现对应的begin()/end()方法,同时保留过期元素清理逻辑:

template <typename Key, typename T, typename Clock = std::chrono::steady_clock>
class expiring_map {
private:
    // 带过期时间的元素结构
    struct mapped_with_expiry {
        expiring_map_value<Key, T> value;
        typename Clock::time_point expiry;
    };

    // multi_index容器定义(按key唯一排序+按过期时间排序)
    using container_type = boost::multi_index::multi_index_container<
        mapped_with_expiry,
        boost::multi_index::indexed_by<
            boost::multi_index::ordered_unique<
                boost::multi_index::member<mapped_with_expiry, expiring_map_value<Key, T>, &mapped_with_expiry::value>,
                [](const auto& a, const auto& b) { return a.first < b.first; }
            >,
            boost::multi_index::ordered_non_unique<
                boost::multi_index::member<mapped_with_expiry, typename Clock::time_point, &mapped_with_expiry::expiry>
            >
        >
    >;

    mutable container_type container_; // mutable允许const方法清理过期元素

    // 清理过期元素的内部方法
    void cleanup_expired() {
        const auto now = Clock::now();
        auto& expiry_idx = container_.template get<1>();
        expiry_idx.erase(expiry_idx.begin(), expiry_idx.upper_bound(now));
    }

public:
    // 迭代器类型定义
    using iterator = typename container_type::template nth_index<0>::type::iterator;
    using const_iterator = expiring_map_const_iterator<typename container_type::template nth_index<0>::type::const_iterator>;

    // 普通迭代器方法
    iterator begin() {
        cleanup_expired();
        return container_.template get<0>().begin();
    }

    iterator end() {
        cleanup_expired();
        return container_.template get<0>().end();
    }

    // const迭代器方法
    const_iterator begin() const {
        cleanup_expired();
        return const_iterator(container_.template get<0>().cbegin());
    }

    const_iterator end() const {
        cleanup_expired();
        return const_iterator(container_.template get<0>().cend());
    }

    // 示例insert方法(结合过期时间逻辑)
    template <typename K, typename V, typename Duration>
    std::pair<iterator, bool> insert(K&& key, V&& value, Duration expiry_duration) {
        cleanup_expired();
        auto [it, inserted] = container_.template get<0>().insert({
            {std::forward<K>(key), std::forward<V>(value)},
            Clock::now() + expiry_duration
        });
        return {it, inserted};
    }

    // 其他std::map风格接口(如find、erase等)可按需实现
};

效果验证

现在你的测试代码:

using subject_type = ::expiring_map<std::shared_ptr<std::chrono::steady_clock>, int, std::string>;
void foo(const subject_type& s) {
   s.begin()->second = "new"; // 编译错误:无法给const对象赋值
}

会触发编译错误,符合预期。同时const expiring_map可以正常用于范围for循环,::testing::ElementsAre也能正常工作,因为代理对象可以隐式转换为expiring_map_value。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 12:57:36