zoukankan      html  css  js  c++  java
  • 如何在C++中的Map或Set中修改Key值

    我们对map中的修改value操作可以是如下的:

    auto myMap = std::map<std::string, int>{ {"one", 1}, {"two", 2}, {"three", 3} };
    myMap.find("two")->second = 22;
    

    但是如果想修改key操作,则下面这段代码是错误的:

    auto myMap = std::map<std::string, int>{ {"one", 1}, {"two", 2}, {"three", 3} };
    myMap.find("two")->first = "dos";
    

    如果是map<int, int>,则会报这样的错误:

    error: assignment of read-only member ‘std::pair<const int, int>::first’
    m.find(1)->first=3;
    

    修改std::map的key值的问题

    std::vectorstd::mapstd::set这些序列容器中,需要提供两个保证:

    • 元素应该以有序的顺序来存储
    • 确保元素是唯一的

    但是在map的情况下,排序顺序很方便,以便找到与对数复杂度的键关联的值。为了保持这些不变性,容器std::map和std::set需要对它们的值在集合内的相对位置进行一些控制。

    如果只是使用迭代器来修改值,例如之前使用的示例,则不会通知该容器。 这将使其结构不一致并破坏不变性。所以是read-only member

    C++17中的key值修改

    修改key的策略是通过容器携带的接口,而不是通过迭代器进行修改

    c++17中给关联容器提供了一个方法:extract,这提供了保存容器元素的节点:

    auto myMap = std::map<std::string, int>{ {"one", 1}, {"two", 2}, {"three", 3} };
     
    auto const node = myMap.extract("two");
    

    extract对容器具有修改作用:映射不再包含节点。 如果我们在调用提取之前和之后检查大小:

    auto myMap = std::map<std::string, int>{ {"one", 1}, {"two", 2}, {"three", 3} };
     
    std::cout << myMap.size() << '
    '; // 3
    auto node = myMap.extract("two");
    std::cout << myMap.size() << '
    '; // 2
    

    所以此时node并不是和map中的元素相关联,所以不会破坏map的结构,修改key值就可以:

    node.key() = "dos";
    

    而这个node并没有提供value的修改方法,因为在map中直接操作会更加方便。

    修改key的值后,需要将node重新插回到map结构中:

    myMap.insert(std::move(node));
    

    这里必须写上std::move,因为node只有移动构造,并没有拷贝构造函数。

    当请求的node并不存在的时候

    auto myMap = std::map<std::string, int>{ {"one", 1}, {"two", 2}, {"three", 3} };
     
    auto node = myMap.extract("four");
    

    node仍然是个有效的对象,我们可以将其用insert插入,但是不能访问它的key();所以最有效的措施应该是在extract之前判断一下是否为空:

    auto myMap = std::map<std::string, int>{ {"one", 1}, {"two", 2}, {"three", 3} };
     
    auto node = myMap.extract("two");
    if (!node.empty())
    {
        node.key() = "dos";
        myMap.insert(std::move(node));
    }
    

    C++17之前怎么做

    在C++17之前,做法就是erase key所在的元素然后再更新后插入,但是这样的做法非常的低效而且不直接。

    封装node

    修改一个map的key值,比提取一个node、修改相应的值、最后插回map中这种表达会更清晰,虽然后者是前者的实现思想。所以我们可以将后者进行封装,抽象为前者的表达:

    template<typename Container>
    void replaceKey(Container& container,
                    const typename Container::key_type& oldKey,
                    const typename Container::key_type& newKey)
    {
        auto node = container.extract(oldKey);
        if(!node.empty())
        {
            node.key() = newKey;
            container.insert(std::move(node));
        }
    }
    
  • 相关阅读:
    【自然框架】终于把源码弄到git上了。
    js的动态加载、缓存、更新以及复用(四)
    ajax的再次封装!(改进版) —— new与不 new 有啥区别?
    ajax的再次封装!
    js的动态加载、缓存、更新以及复用(三)
    js的动态加载、缓存、更新以及复用(二)
    js的动态加载、缓存、更新以及复用(一)
    以【猫叫、老鼠跑、主人醒】为例子,使用 javascript 来实现 观察者模式 (有在线演示)
    以【联动列表框】来看单一职责!
    CPU 多核指令 —— WFE 原理【原创】
  • 原文地址:https://www.cnblogs.com/yunlambert/p/13620363.html
Copyright © 2011-2022 走看看