是否可以从std :: set中删除修改后的元素?

时间:2015-07-14 07:48:43

标签: c++ stl set

我需要一个可以修改元素的有序集合。 修改后擦除元素是否安全?排序键可以修改。

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;
}

8 个答案:

答案 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'

*ite是两个不同的对象。

通常,STL set / map迭代器指向的对象和用作搜索键的对象是不同的对象。因此,您仍然会处理容器对象的副本

现在你的其他问题归结为两个:

  • 我可以修改itr指向的对象吗?它会影响排序顺序吗?

    • 理论上可行。集合的内容是const,迭代器不允许直接赋值 -

      test.cpp10:9: error: read-only variable is not assignable.
      *it = 4;
      ~~~ ^
      

      - 但是使用mutableconst_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.
相关问题