C++ std::set 更新很乏味:我无法就地更改元素
我发现 std::set
上的更新操作很乏味,因为 cppreference。所以我目前所做的事情是这样的:
//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
I find the update operation on std::set
tedious since there's no such an API on cppreference. So what I currently do is something like this:
//find element in set by iterator
Element copy = *iterator;
... // update member value on copy, varies
Set.erase(iterator);
Set.insert(copy);
Basically the iterator return by Set
is a const_iterator
and you can't change its value directly.
Is there a better way to do this? Or maybe I should override std::set
by creating my own (which I don't know exactly how it works..)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我在 C++11 中遇到了同样的问题,其中
::std::set::iterator
确实是常量,因此不允许更改其内容,即使我们知道变换不会影响<
不变量。您可以通过将::std::set
包装成mutable_set
类型或为内容编写一个包装器来解决这个问题:我发现这更简单,并且它可以在 90% 的情况下工作在这种情况下,用户甚至没有注意到集合和实际类型之间存在某些东西。
I encountered the very same issue in C++11, where indeed
::std::set<T>::iterator
is constant and thus does not allow to change its contents, even if we know the transformation will not affect the<
invariant. You can get around this by wrapping::std::set
into amutable_set
type or write a wrapper for the content:I find this much simpler and it works in 90% the cases without the user even noticing there to be something between the set and the actual type.
在某些情况下,这会更快:
如果该值通常不在
std::set
中,那么这可以具有更好的性能。This is faster in some cases:
If the value is usually not already in the
std::set
then this can have better performance.set
返回const_iterators
(标准规定set::iterator
是const
,并且set::const_iterator
和set::iterator
实际上可能是相同的类型 - 请参阅 23.2.4/6 n3000.pdf),因为它是一个有序的容器。如果它返回常规迭代器
,您将被允许从容器下更改项目值,这可能会改变顺序。您的解决方案是更改
set
中的项目的惯用方法。set
returnsconst_iterators
(the standard saysset<T>::iterator
isconst
, and thatset<T>::const_iterator
andset<T>::iterator
may in fact be the same type - see 23.2.4/6 in n3000.pdf) because it is an ordered container. If it returned a regulariterator
, you'd be allowed to change the items value out from under the container, potentially altering the ordering.Your solution is the idiomatic way to alter items in a
set
.C++17 引入了
extract
,请参阅 Barry 的回答。如果您坚持使用旧版本,有两种方法可以做到这一点,在简单的情况下:
mutable
Key
Value
对(并使用std::map
)现在,问题是针对棘手的情况:更新实际时会发生什么修改对象的
key
部分?你的方法很有效,尽管我承认这很乏味。C++17 introduced
extract
, see Barry's answer.If you're stuck with an older version, there are 2 ways to do this, in the easy case:
mutable
on the variable that are not part of the keyKey
Value
pair (and use astd::map
)Now, the question is for the tricky case: what happens when the update actually modifies the
key
part of the object ? Your approach works, though I admit it's tedious.在 C++17 中,您可以使用
extract()
,感谢 P0083:这将避免您的类型的额外副本和额外的分配/解除分配, 和也适用于仅移动类型。
您还可以通过按键
提取
。如果键不存在,这将返回一个空节点:In C++17 you can do better with
extract()
, thanks to P0083:This will avoid an extra copy of your type and an extra allocation/deallocation, and will also work with move-only types.
You can also
extract
by key. If the key is absent, this will return an empty node:更新:尽管目前以下情况正确,但该行为被视为缺陷并将在即将发布的标准版本中进行更改。多么悲伤啊。
有几点使您的问题相当令人困惑。
std::set
是一个类,因此不能返回任何内容。s.erase(iter)
,那么iter
就不是const_iterator
。erase
需要一个非常量迭代器。只要更新不改变元素的顺序,您就可以更改集合中元素的值。下面的代码编译并运行得很好。
如果您的更新更改了元素的顺序,则必须擦除并重新插入。
Update: Although the following is true as of now, the behavior is considered a defect and will be changed in the upcoming version of the standard. How very sad.
There are several points that make your question rather confusing.
std::set
is a class, and therefore cannot return anything.s.erase(iter)
, theniter
is not aconst_iterator
.erase
requires a non-const iterator.std::set
that return an iterator return a non-const iterator as long as the set is non-const as well.You are allowed to change the value of an element of a set as long as the update doesn't change the order of elements. The following code compiles and works just fine.
If your update changes the order of elements, then you have to erase and reinsert.
您可能想使用
std::map
来代替。使用Element
中影响键排序的部分,并将所有Element
作为值。将会有一些小的数据重复,但您将获得更容易(并且可能更快)的更新。You may want to use an
std::map
instead. Use the portion ofElement
that affects the ordering the key, and put all ofElement
as the value. There will be some minor data duplication, but you will have easier (and possibly faster) updates.