我发现std::set
上的更新操作很乏味,因为cppreference上没有这样的API。所以我现在做的是这样的事情:
//find element in set by iterator
Element copy = *iterator;
... // update member value on copy, varies
Set.erase(iterator);
Set.insert(copy);
基本上Set
返回的迭代器是const_iterator
,你不能直接改变它的值。
有更好的方法吗?或者我可以通过创建自己的std::set
来覆盖{{1}}(我不确切知道它是如何工作的......)
答案 0 :(得分:70)
set
返回const_iterators
(标准显示set<T>::iterator
为const
,而set<T>::const_iterator
和set<T>::iterator
实际上可能属于同一类型 - 参见n3000.pdf中的23.2.4 / 6)因为它是一个有序的容器。如果它返回常规iterator
,您将被允许从容器下面更改项值,可能会改变排序。
您的解决方案是改变set
中项目的惯用方法。
答案 1 :(得分:22)
在简单的情况下,有两种方法可以做到这一点:
mutable
Key
Value
对中拆分课程(并使用std::map
)现在,问题是针对棘手的情况:当更新实际修改对象的key
部分时会发生什么?你的方法有效,但我承认这很乏味。
答案 2 :(得分:8)
更新:虽然现在的情况如下,但该行为被视为defect,并将在即将发布的标准版本中进行更改。多么难过。
有几点让你的问题相当混乱。
std::set
是一个类,因此无法返回任何内容。s.erase(iter)
,则iter
不是const_iterator
。 erase
需要非const迭代器。std::set
的所有成员函数返回非const迭代器,只要该集合也是非const。只要更新不改变元素的顺序,就可以更改集合元素的值。以下代码编译并正常工作。
#include <set>
int main()
{
std::set<int> s;
s.insert(10);
s.insert(20);
std::set<int>::iterator iter = s.find(20);
// OK
*iter = 30;
// error, the following changes the order of elements
// *iter = 0;
}
如果您的更新更改了元素的顺序,则必须擦除并重新插入。
答案 3 :(得分:7)
您可能希望使用std::map
代替。使用影响键排序的Element
部分,并将所有Element
作为值。将会有一些小的数据重复,但你会有更容易(也可能更快)的更新。
答案 4 :(得分:6)
在C ++ 17中,借助extract()
,P0083可以做得更好:
// remove element from the set, but without needing
// to copy it or deallocate it
auto node = Set.extract(iterator);
// make changes to the value in place
node.value() = 42;
// reinsert it into the set, but again without needing
// to copy or allocate
Set.insert(std::move(node));
这将避免您的类型的额外副本和额外的分配/取消分配,并且还将与仅移动类型一起使用。
您也可以通过键extract
。如果没有密钥,将返回一个空节点:
auto node = Set.extract(key);
if (node) // alternatively, !node.empty()
{
node.value() = 42;
Set.insert(std::move(node));
}
答案 5 :(得分:2)
我在C ++ 11中遇到了同样的问题,其中::std::set<T>::iterator
确实是常量,因此不允许更改其内容,即使我们知道转换不会影响<
不变量。您可以通过将::std::set
包装到mutable_set
类型或为内容编写包装来解决此问题:
template <typename T>
struct MutableWrapper {
mutable T data;
MutableWrapper(T const& data) : data(data) {}
MutableWrapper(T&& data) : data(data) {}
MutableWrapper const& operator=(T const& data) { this->data = data; }
operator T&() const { return data; }
T* operator->() const { return &data; }
friend bool operator<(MutableWrapper const& a, MutableWrapper const& b) {
return a.data < b.data;
}
friend bool operator==(MutableWrapper const& a, MutableWrapper const& b) {
return a.data == b.data;
}
friend bool operator!=(MutableWrapper const& a, MutableWrapper const& b) {
return a.data != b.data;
}
};
我发现这个更简单,它可以在90%的情况下工作,而用户甚至没有注意到集合和实际类型之间有什么东西。
答案 6 :(得分:0)
在某些情况下这会更快:
std::pair<std::set<int>::iterator, bool> result = Set.insert(value);
if (!result.second) {
Set.erase(result.first);
Set.insert(value);
}
如果该值通常不在std::set
中,那么这可以有更好的效果。