代码之家  ›  专栏  ›  技术社区  ›  Miral

使用迭代器替换映射中常量项的方法

  •  -1
  • Miral  · 技术社区  · 6 年前

    我有这样的代码:

    std::map<int, const Data> all_data;
    // ...
    bool OldDataIsBetter(Data const& oldData, Data const& newData) {...}
    // ...
    void AddData(int key, Data&& newData)
    {
        auto [it, ok] = all_data.try_emplace(key, std::move(newData));
        if (!ok)
        {
            if (OldDataIsBetter(*it, newData)) return;
            it->second = std::move(newData);
        }
    }
    

    这不会编译,因为 it->second const Data 因此不能调用它的赋值运算符。如果 const 被移除。

    上述内容的目的是: insert_or_assign ,但如果一个项目已经存在,那么我需要比较新旧项目,看看哪一个“更好”。

    数据在映射中应该是不可变的——项目作为一个整体可以被替换,但不能逐段修改。

    我可以“修复”上面的问题 通过重新分配到容器:

    all_data[key] = std::move(newData);
    

    (事实证明,这与 .)

    或者通过删除并重试安放:

    all_data.erase(it);
    all_data.emplace(key, std::move(newData)); // should never fail
    

    但这两种方法看起来都不优雅,因为我已经有一个迭代器指向应该被替换的项,而上面两种方法都忘记了这一点,然后再次搜索。

    是否有更好的方法来完成此替换?


    来自聊天线程的TLDR提出了相关问题:

    • 如果 extract key ,并重新插入它--所有这些都没有任何重新分配--为什么对于常量映射的_值不可能这样做?
    • replace 是否使用不同的常量映射_值创建常量映射_值,而不重新分配包含该值的映射节点?
    2 回复  |  直到 6 年前
        1
  •  1
  •   Richard Hodges    6 年前

    这强烈支持可变数据元素。

    在一般情况下,容器必须具有不可变元素,因此您可以提供提供常量访问的包装器,除非特别请求可变访问:

    #include <map>
    #include <functional>
    
    struct Data {};
    
    struct MyDataMap
    {
        using store_type = std::map<int, Data>;
        using iterator = store_type::const_iterator;
    
        template<class Condition>
        std::pair<iterator, bool> 
        replace_if(int key, Data&& value, Condition cond)
        {
            auto [it, inserted_or_replaced] = store_.try_emplace(key, std::move(value));
            if (!inserted_or_replaced && cond(it->second, value))
            {
                it->second = std::move(value);
                inserted_or_replaced = true;
            }
            return std::make_pair(it, inserted_or_replaced);
        }
    
        // other accessors as necessary
    
        iterator begin() const { return store_.cbegin(); }
        iterator end() const { return store_.cend(); }
    
    private:
    
        store_type store_;
    };
    
    // ...
    bool OldDataIsBetter(Data const& oldData, Data const& newData);
    // ...
    
    void test(MyDataMap& m, int k, Data newd)
    {
        auto oldIsWorse = std::not_fn(OldDataIsBetter);
        auto [it, replaced] = m.replace_if(k, std::move(newd), oldIsWorse);    
    }
    
        2
  •  2
  •   Nicol Bolas    6 年前

    const 意思是不可变的,不是部分可变的。如果你使用 在对象声明上(当您坚持 在那个模板参数中,C++相信你 真的吗 . 它会让你坚持下去。

    const Data 这是一个非初学者。

    Data

    实现这一点的方法是提供一个对象类型,该对象类型封装 资料 实例,允许从 资料 对象此包装器将提供 常数 访问器 资料 提供。但是,该类型不提供非- 常数 资料

        3
  •  0
  •   Miral    6 年前

    it = all_data.erase(it);
    all_data.emplace_hint(it, key, std::move(newData)); // should never fail
    

    这对我来说仍然不太理想,因为它仍然会重新分配映射节点。这不是世界末日,但如果可能的话,我想找到一种避免它的方法。