[英]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;
}
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
提供标准关联容器不会, replace
和modify
两个成员。 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.