C++ std::set 更新很乏味:我无法就地更改元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2217878/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ std::set update is tedious: I can't change an element in place
提问by Figo
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:
我发现更新操作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);
Basically the iterator return by Set
is a const_iterator
and you can't change its value directly.
基本上迭代器返回的Set
是 aconst_iterator
并且你不能直接改变它的值。
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..)
有一个更好的方法吗?或者也许我应该std::set
通过创建自己的来覆盖(我不知道它是如何工作的......)
回答by Terry Mahaffey
set
returns const_iterators
(the standard says set<T>::iterator
is const
, and that set<T>::const_iterator
and set<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 regular iterator
, you'd be allowed to change the items value out from under the container, potentially altering the ordering.
set
返回const_iterators
(标准说set<T>::iterator
是const
,这set<T>::const_iterator
并set<T>::iterator
可能实际上是相同类型的-见23.2.4 / 6在n3000.pdf),因为它是一个有序的容器。如果它返回一个常规的iterator
,您将被允许从容器下更改 items 值,这可能会改变排序。
Your solution is the idiomatic way to alter items in a set
.
您的解决方案是更改set
.
回答by Matthieu M.
There are 2 ways to do this, in the easy case:
有两种方法可以做到这一点,在简单的情况下:
- You can use
mutable
on the variable that are not part of the key - You can split your class in a
Key
Value
pair (and use astd::map
)
- 您可以
mutable
在不属于密钥的变量上使用 - 您可以将班级分成一
Key
Value
对(并使用std::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.
现在,问题是针对棘手的情况:当更新实际修改key
对象的一部分时会发生什么?您的方法有效,尽管我承认这很乏味。
回答by Barry
In C++17 you can do better with extract()
, thanks to P0083:
在 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));
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:
也可以extract
按键。如果键不存在,这将返回一个空节点:
auto node = Set.extract(key);
if (node) // alternatively, !node.empty()
{
node.value() = 42;
Set.insert(std::move(node));
}
回答by avakar
Update:Although the following is true as of now, the behavior is considered a defectand will be changed in the upcoming version of the standard. How very sad.
更新:尽管目前以下情况属实,但该行为被视为缺陷,并将在即将发布的标准版本中进行更改。多么悲伤。
There are several points that make your question rather confusing.
有几点让你的问题相当混乱。
- Functions can return values, classes can't.
std::set
is a class, and therefore cannot return anything. - If you can call
s.erase(iter)
, theniter
is not aconst_iterator
.erase
requires a non-const iterator. - All member functions of
std::set
that return an iterator return a non-const iterator as long as the set is non-const as well.
- 函数可以返回值,类不能。
std::set
是一个类,因此不能返回任何东西。 - 如果可以调用
s.erase(iter)
,那就iter
不是了const_iterator
。erase
需要一个非常量迭代器。 std::set
只要集合也是非常量的,它的所有成员函数都返回一个迭代器,返回一个非常量迭代器。
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.
只要更新不改变元素的顺序,您就可以更改集合中元素的值。下面的代码编译并运行得很好。
#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;
}
If your update changes the order of elements, then you have to erase and reinsert.
如果您的更新更改了元素的顺序,则您必须擦除并重新插入。
回答by avakar
You may want to use an std::map
instead. Use the portion of Element
that affects the ordering the key, and put all of Element
as the value. There will be some minor data duplication, but you will have easier (and possibly faster) updates.
您可能想std::map
改用 。使用Element
影响键排序的部分,并将所有Element
作为值。会有一些轻微的数据重复,但您将获得更容易(并且可能更快)的更新。
回答by bitmask
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 a mutable_set
type or write a wrapper for the content:
我在 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;
}
};
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.
我发现这要简单得多,它在 90% 的情况下都有效,用户甚至没有注意到集合和实际类型之间存在某些东西。
回答by jcoffland
This is faster in some cases:
在某些情况下这更快:
std::pair<std::set<int>::iterator, bool> result = Set.insert(value);
if (!result.second) {
Set.erase(result.first);
Set.insert(value);
}
If the value is usually not already in the std::set
then this can have better performance.
如果该值通常不在 中,std::set
那么这可以有更好的性能。