我需要一个可以修改元素的有序集合。 修改后擦除元素是否安全?排序键可以修改。
auto it=s.find(e)
modify(e)
s.erase(it)
我在VS2010上做了一些测试,但它确实有效。我认为擦除(它)不需要搜索元素,因此不需要在被擦除的元素上调用compare。
很难修改整个程序以在修改之前删除元素,这就是我正在寻找替代解决方案的原因。
编辑:添加工作样本以使其更清晰
#include <iostream>
#include <algorithm>
#include <set>
template <typename T>
struct PtrCmp
{
bool operator()(const T* x, const T* y) const
{
return *x<*y;
}
};
int main()
{
std::set<int*, PtrCmp<int>> aset;
int t[]={1,2,3,4};
for(int i=0;i<4;++i)
aset.insert(&t[i]);
auto it=aset.find(&t[2]);
t[2]=5;
aset.erase(it);
for(auto it=aset.begin(); it!=aset.end(); ++it)
std::cout<<**it<<std::endl;
}
答案 0 :(得分:1)
在您的示例t[2]=5;
中,修改比较器中使用的值,例如用于重新平衡set
容器后面的树数据结构。因此,这种修改是不安全的,因为在节点处的事实密钥被改变的情况下,平衡树算法可能失败,因此与该树节点的期望不匹配。 erase
操作可能会触发树重新平衡,这就是您获取未定义行为的方式。因此,通过修改比较器中使用的值,您实际上会破坏set
容器后面树的平衡性。
答案 1 :(得分:0)
modify(e)
(也许)不会修改该集。我的猜测是e
是集合之外的东西,s.find(e)
在集合中搜索e
的副本。基本上,您的代码相当于:
auto it=s.find(e)
s.erase(it)
modify(e)
所以不会有任何问题。
除非e
实际上是指向数据的原始指针。
答案 2 :(得分:0)
std::set<T>
中的元素被视为const
。如果您更改元素,例如,通过使用mutable
成员或指向某些内部数据的指针并使用该键,则会违反std::set<T>
的不变量,并且在访问时您将获得未定义的行为<{1}}以任何方式。
答案 3 :(得分:0)
集合中的元素不会(或不应该)与e
有任何关系。如果您修改e
并且它不会影响设置元素,那么您没问题。
如果修改确实以修改其排序顺序的方式影响了set元素,那么此时您在技术上已经获得了未定义的行为,之后不再需要erase
。
见C ++ 11 23.2.4第4和第5段。
答案 4 :(得分:0)
除非modify()
执行invalidates the iterator的任何操作,否则它是安全的。如果它无法访问s
变量,则没有问题。
答案 5 :(得分:0)
您的代码是正确的。该集包含四个指针。它不包含那些指针指向的东西。
t[2] = 5;
不会以任何方式修改set
。
答案 6 :(得分:0)
用语言表达:
// I have this key 'e'...
auto it=s.find(e) // I look through my set for another like 'e'
// and 'it' now points to it
modify(e) // now I 'invert' e.
s.erase(it) // and I remove object pointed to by 'it'
*it
和e
是两个不同的对象。
通常,STL set / map迭代器指向的对象和用作搜索键的对象是不同的对象。因此,您仍然会处理容器对象的副本。
现在你的其他问题归结为两个:
我可以修改itr指向的对象吗?它会影响排序顺序吗?
理论上可行。集合的内容是const,迭代器不允许直接赋值 -
test.cpp10:9: error: read-only variable is not assignable.
*it = 4;
~~~ ^
- 但是使用mutable
或const_cast
和pointer-magic的组合,可以修改const数据。它是危险吗? You betcha。每个容器都有自己的一组假设,其API保持不变。因此,一般规则 - 从不 - 对其API不允许的STL容器执行操作。
我可以获得一个可以修改其元素的已排序容器吗?
当然!你只需在容器外面做,然后把它放回去。
auto it=s.find(e);
// possible optimisation (see below)
s.erase(it);
modify(e);
auto position = s.insert(e);
if (position.first) // no existing values matching e's comparison!
assert(*position.second.key == e.key);
使用C ++ 11,根据'e'的不同,您可以使用move-semantics将非比较元素从* it移动到e,与set emplace一起移动以优化副本数完成。
答案 7 :(得分:0)
在您的示例中,您修改了set
的顺序,因此具有未定义的行为(并且&#39;正常工作&#39;是一种可能的行为: - /)。
您可以使用以下代码获得正确的代码:
auto it = aset.find(&t[2]);
assert(it != aset.end()); // or any other error check methods
aset.erase(it);
t[2] = 5; // No longer used by set
或
auto it = aset.find(&t[2]);
assert(it != aset.end()); // or any other error check methods
auto* ref = *it;
aset.erase(it);
assert(ref != nullptr); // or any other error check methods
*ref = 5; // No longer used by set.