开发者

Deleting element from one set by calling erase(iterator) on another set. Is this normal behavior?

开发者 https://www.devze.com 2023-01-29 03:55 出处:网络
I found out that an element from one set can be deleted from another set by using the erase method that accepts an iterator. I can\'t explain why this happens, and I would like to know if this is norm

I found out that an element from one set can be deleted from another set by using the erase method that accepts an iterator. I can't explain why this happens, and I would like to know if this is normal behavior or not.

So, lets set the scene:

I have a set of objects that I separated in two STL sets, for logical reasons (that is, they are disjoint sets).

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

The set_a contains the objects I want. If this object is not found in set_a, an empty one is created and inserted into set_b. I then perform some calculations on the wanted object, and if a certain condition is met, I delete it.

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
}

So I was wondering how I would delete this object. Since I have the iterator, I thought of deleting using it directly. But what happens when you use erase with an iterator "pointing" to an object in another set? There is nothing on the documentation I use (http://www.cplusplus.com/reference/stl/set/erase/) so I did the following test.

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

This test results in the following exit:

Initial set
1 2 4 
Set after insertion
1开发者_StackOverflow 2 3 4 
Set after erasure
1 2 4 

I found it very strange that an element from s could be deleted by calling the method from s2, so I executed my program with valgrind. No errors there. I think I would actually benefit from this behavior, since I don't need to check which set contains the element, but I wonder if this is normal behavior or not.

Thanks!


This might work because of how your vendor happens to implement std::set<T> and std::set<T>::iterator, but it's not guaranteed to.

Standard section 23.1.2 Paragraph 7 and Table 69 say that the expression a.erase(q) is valid when a is an object of an associative container class type (set is an associative container) and "q denotes a valid dereferenceable iterator to a".

When the iterator is not from the container, you get Undefined Behavior. Appearing to work is one valid result of Undefined Behavior.


I'm fairly convinced that iterators from different containers should not be mixed in this way, and that it results in the dreaded undefined behavior.

So, what you're seeing is pure luck and chance, that works on your compiler's STL implementation, and at the current phase of the moon. I wouldn't bet on it working anywhere or anytime else.


How about using a local pointer to a std::set<Obj>?

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

精彩评论

暂无评论...
验证码 换一张
取 消