简体   繁体   English

修改std :: map的键

[英]Modify key of std::map

Is there a way to modify the key of a std::map or ? 有没有办法修改std::map的键或? This example shows how to do so with rebalancing the tree. 此示例显示如何通过重新平衡树来执行操作。 But what if I provide some guarantees that the key won't need to be rebalanced? 但是如果我提供一些保证不需要重新平衡密钥呢?

#include <vector>
#include <iostream>
#include <map>


class Keymap
{
private:    
    int key; // this key will be used for the indexing
    int total;
public:
    Keymap(int key): key(key), total(0)
    {}
    bool operator<(const Keymap& rhs) const{
        return key < rhs.key;
    }
    void inc()
    {
        total++;
    }
};
std::map<Keymap, int> my_index;



int main (){
    std::map<Keymap, int> my_index;
    Keymap k(2);
    my_index.insert(std::make_pair(k, 0));

    auto it = my_index.begin();

    it->first.inc(); // this won't rebalance the tree from my understanding
    return 0;
}

The modification won't compile because of the constness of it->first 由于it->first的常量,修改将无法编译

Is there any way to override this behavior? 有没有办法覆盖这种行为?

You could make inc const and total mutable 你可以使inc const和total变得可变

class Keymap
{
private:    
    int key; // this key will be used for the indexing
    mutable int total;
public:
    Keymap(int key): key(key), total(0)
    {}
    bool operator<(const Keymap& rhs) const{
        return key < rhs.key;
    }
    void inc() const
    {
        total++;
    }
};

But you do need to ask yourself why you are doing this, mutable isn't used much. 但是你确实需要问问自己为什么要这样做, mutable不是很多。

You're right that no rebalancing is going to happen. 你是对的,没有再平衡会发生。

If you cannot change the design and introduce surrogate read-only keys, your best option is to use Boost.MultiIndex container (I am not aware of reasonable alternatives). 如果你不能改变设计并引入代理只读密钥,你最好的选择是使用Boost.MultiIndex容器(我不知道合理的替代方案)。 It is designed specifically for this purpose and has consistent built-in support of updating the indexed object, including the transactional variant. 它专门为此目的而设计,并且具有更新索引对象(包括事务变体)的一致内置支持。 Documentation and code examples are here . 文档和代码示例在这里

Generally, patterns like storing business entities in a self-keyed sets, having mutable keys serving additional purpose (counters and whatnot), etc. tend to have impact on maintenability, performance, and scalability of the code. 通常,诸如将业务实体存储在自键控集合中,具有用于附加目的的可变密钥(计数器和诸如此类)等的模式往往会影响代码的可维护性,性能和可伸缩性。

You could wrap your keys into a class that allows modification of const objects. 您可以将键包装到允许修改const对象的类中。 One such class would be std::unique_ptr : 一个这样的类将是std::unique_ptr

using KeymapPtr = std::unique_ptr<Keymap>;

struct PtrComp
{
    template<class T>
    bool operator()(const std::unique_ptr<T>& lhs, const std::unique_ptr<T>& rhs) const
    {
        return *lhs < *rhs;
    }
};

template<class V>
using PtrMap = std::map<KeymapPtr, V, PtrComp>;

int main (){
    PtrMap<int> my_index;
    KeymapPtr k = std::make_unique<Keymap>(2);
    my_index.emplace(std::move(k), 0);

    auto it = my_index.begin();

    it->first->inc(); // this won't rebalance the tree from my understanding
    return 0;
}

Demo 演示

Note that we have to supply a custom comparator object since we (presumably) want to sort by the key values, not the pointer values. 请注意,我们必须提供自定义比较器对象,因为我们(可能)想要按键值排序,而不是按指针值排序。

To be clear, this is not what unique_ptr is meant for, and the const semantics of smart pointers (which follow those of regular pointers) are a bit backwards from this perspective (why can I get a non- const reference from a const object? A linter may complain about this kind of use...), but it does the trick here. 要清楚,这不是unique_ptr的意思,智能指针的const语义(遵循常规指针的那些)从这个角度来看有点倒退(为什么我可以从const对象获得非const引用?一个linter可能会抱怨这种用途...),但它在这里做的伎俩。 The same would of course work with naked pointers (where a T* const can have the T value changed but not the pointer location, whereas a const T* can have its location changed but not the T ), but this mimics the ownership/lifetime model of your original code. 同样适用于裸指针(其中T* const可以改变T值但不改变指针位置,而const T*可以改变其位置但不改变T ),但这模仿了所有权/生命周期原始代码的模型。

Needless to say, this opens the door to breaking the map invariants (breaking the sortedness by keys) so think twice before using it. 毋庸置疑,这打开了打破地图不变量的大门(通过键打破排序),所以在使用之前要三思而后行。 But unlike const_cast ing your key directly, it is free of UB. 但与const_cast直接键不同,它不含UB。

std::map and the other standard associative containers do not provide a way to do this without removing and adding an element, likely causing tree rebalancing side effects. std::map和其他标准关联容器在没有删除和添加元素的情况下不提供这样做的方法,可能导致树重新平衡副作用。 You can go around the map key constness in various ways (eg using mutable members), but then it's entirely up to you to make sure you don't actually break the key ordering. 您可以通过各种方式绕过地图键constness(例如使用mutable成员),但这完全取决于您确保您实际上不会破坏键排序。

If you need this sort of efficiency but a bit more safety, you might consider changing the container to a boost::multi_index_container instead. 如果您需要这种效率但更安全,可以考虑将容器更改为boost::multi_index_container

A std::map<K,V> is similar to: std::map<K,V>类似于:

namespace BMI = boost::multi_index;
using map_value_type = std::pair<K, V>;
using map_type = BMI::multi_index_container<
    map_value_type,
    BMI::indexed_by<BMI::ordered_unique<
        BMI::member<map_value_type, &map_value_type::first>
>>>;

except that in a multi_index_container , the entire element is always const . 除了在multi_index_container ,整个元素总是const If you want to be able to directly modify the second members, a means for that is described on this boost page . 如果您希望能够直接修改second成员,则可以在此增强页面上描述其中的一种方法。

multi_index_container provides two members the standard associative containers do not, replace and modify . multi_index_container提供标准关联容器不会, replacemodify两个成员。 Both of these will check for whether the modified element is in the same sort order or not. 这两个都将检查修改的元素是否在相同的排序顺序。 If it is, no rebalancing is done. 如果是,则不进行重新平衡。

auto it = my_index.begin();
auto pair = *it;
pair.first.inc();
my_index.replace(it, pair);

// OR

auto it = my_index.begin();
my_index.modify(it, [](auto& pair) { pair.first.inc(); });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM