通过在另一个集上调用erase(iterator)来从一个集合中删除元素。这是正常的行为吗?

时间:2010-12-07 23:57:52

标签: c++ stl iterator set

我发现可以使用接受迭代器的erase方法从另一个集合中删除一个集合中的元素。我无法解释为什么会发生这种情况,我想知道这是否是正常行为。

所以,让我们设置场景:

由于逻辑原因(即,它们是不相交的集合),我有一组在两个STL集中分隔的对象。

std::set<Obj> set_a;
std::set<Obj> set_b;

set_a包含我想要的对象。如果在set_a中找不到此对象,则会创建一个空对象并将其插入set_b。然后我对想要的对象执行一些计算,如果满足某个条件,我将其删除。

std::set<Obj>::iterator it = set_a.find(o);
std::set<Obj>::iterator end = set_a.end();

if (it == end) {
    it = set_b.lower_bound();
    end = set_b.end();

    if (it == end || *it != o) {
        it = set_b.insert(it, o);
    }
}

// do calculations with it
if (/*condition is met*/) {
    // erase it
}

所以我想知道如何删除这个对象。由于我有迭代器,我想删除直接使用它。但是当你使用擦除迭代器“指向”另一组中的对象时会发生什么?我使用的文档中没有任何内容(http://www.cplusplus.com/reference/stl/set/erase/),所以我做了以下测试。

#include <iostream>
#include <iterator>
#include <algorithm>
#include <set>
#include <sstream>

// streams
using std::cout;
using std::ostream_iterator;
using std::ostringstream;
// data structures
using std::set;
// algorithms
using std::copy;

int main() {
    set<int> s, s2;
    s.insert(1);
    s.insert(2);
    s.insert(4);

    cout << "Initial set\n";
    // print set elements
    copy(s.begin(), s.end(), ostream_iterator<int> (cout, " "));
    cout << "\n";

    set<int>::iterator s_it = s.lower_bound(3);

    if (s_it == s.end() || *s_it != 3) {
        s_it = s.insert(s_it, 3);
    }

    cout << "Set after insertion\n";
    // print set elements
    copy(s.begin(), s.end(), ostream_iterator<int> (cout, " "));
    cout << "\n";

    // erase element from another set
    s2.erase(s_it);

    cout << "Set after erasure\n";
    // print set elements
    copy(s.begin(), s.end(), ostream_iterator<int> (cout, " "));
    cout << "\n";

    return 0;
}

此测试导致以下退出:

Initial set
1 2 4 
Set after insertion
1 2 3 4 
Set after erasure
1 2 4 

我发现很奇怪来自s的元素可以通过从s2 调用方法来删除,所以我用valgrind执行了我的程序。没有错误。我想我实际上会受益于这种行为,因为我不需要检查哪个集合包含该元素,但我想知道这是否是正常行为。

谢谢!

3 个答案:

答案 0 :(得分:8)

这可能会起作用,因为您的供应商实施std::set<T>std::set<T>::iterator的方式,但不能保证。

标准部分23.1.2第7段和表69表示当a.erase(q)是关联容器类类型的对象(a是关联容器)时,表达式set有效。并且“q表示a”的有效可解引用迭代器。

当迭代器不是来自容器时,您将获得未定义的行为。看似工作是未定义行为的一个有效结果。

答案 1 :(得分:2)

我相信来自不同容器的迭代器不应该以这种方式混合,并且它会导致可怕的未定义的行为

所以,你所看到的是纯粹的运气和机会,它适用于你的编译器的STL实现,以及当前的月球阶段。我不打算在任何地方或任何其他地方工作。

答案 2 :(得分:0)

如何使用指向std :: set&lt; Obj&gt;的本地指针?

std::set<Obj> set_a;
std::set<Obj> set_b;
std::set<Obj>* current_set = &set_a;

std::set<Obj>::iterator it = current_set->find(o);
std::set<Obj>::iterator end = current_set->end();

if (it == end) {
    current_set = &set_b;
    it = current_set->lower_bound();
    end = current_set->end();

    if (it == end || *it != o) {
        it = current_set->insert(it, o);
    }
}

// do calculations with it
if (/* condition met */) {
   current_set->erase(it);
}