2010-12-07 4 views
0

Ich fand heraus, dass ein Element aus einer Menge aus einer anderen Menge gelöscht werden kann, indem die Erase-Methode verwendet wird, die einen Iterator akzeptiert. Ich kann nicht erklären, warum das passiert, und ich würde gerne wissen, ob das normal ist oder nicht.Löschen von Elementen aus einer Gruppe durch Aufrufen von Erase (Iterator) auf einer anderen Gruppe. Ist das normales Verhalten?

So läßt die Szene:

Ich habe eine Reihe von Objekten, die ich in zwei STL-Sets getrennt, aus logischen Gründen (das heißt, sie sind disjunkte Mengen).

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

Die set_a enthält die Objekte, die ich möchte. Wenn dieses Objekt nicht in set_a gefunden wird, wird ein leeres Objekt erstellt und in set_b eingefügt. Ich führe dann einige Berechnungen für das gewünschte Objekt durch, und wenn eine bestimmte Bedingung erfüllt ist, lösche ich es.

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 
} 

Also fragte ich mich, wie ich dieses Objekt löschen würde. Da ich den Iterator habe, dachte ich daran, ihn direkt zu löschen. Was passiert aber, wenn Sie Erase mit einem Iterator verwenden, der auf ein Objekt in einem anderen Set "zeigt"? Es gibt nichts in der Dokumentation, die ich verwende (http://www.cplusplus.com/reference/stl/set/erase/), also habe ich den folgenden Test gemacht.

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

Dieser Test ergibt folgende Ausfahrt:

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

Ich fand es sehr merkwürdig, dass ein Element von s durch den Aufruf der Methode von s2 gelöscht werden konnte, so ausgeführt ich mein Programm mit Valgrind. Keine Fehler dort. Ich denke, dass ich von diesem Verhalten tatsächlich profitieren würde, da ich nicht überprüfen muss, welcher Satz das Element enthält, aber ich frage mich, ob das normales Verhalten ist oder nicht.

Danke!

+0

Ich bin mir nicht sicher, welche Schlussfolgerung Sie zeichnen. Sie setzen das Element in s und löschten es aus s, indem Sie einen Iterator verwenden, der auf das Element in s verweist. s2 hatte wirklich nichts mit irgendetwas zu tun. –

+0

@Anon, sagst du, dass der Aufruf von s2.erase() mit einem Iterator auf s funktioniert, vorausgesetzt, dass s und s2 vom selben Typ sind? –

+0

@Tony: Ich sage das nicht. Ich sage nur, dass "das Löschen eines Elements in A von einem Iterator in B" überhaupt nicht das ist, was hier passiert. –

Antwort

8

Dies funktioniert möglicherweise, weil Ihr Anbieter implementiert std::set<T> und std::set<T>::iterator passiert, aber es ist nicht garantiert.

Norm Abschnitt 23.1.2 Ziffer 7 und Tabelle 69 sagen, daß der Ausdruck a.erase(q) gilt, wenn ein Objekt a einen assoziativen Container-Klasse-Typs ist (set ist ein assoziativer Behälter) und „q bezeichnet einen gültigen dereferenceable Iterator a“ .

Wenn der Iterator nicht aus dem Container stammt, wird das Verhalten "Undefiniert" zurückgegeben. Zur Arbeit erscheint ein gültiges Ergebnis von Undefined Behavior.

+1

+1 für die Angabe des Standards. – Macke

+1

Vorsicht beim Schreiben von Code, der auf undefiniertem Verhalten beruht. Es endet nie gut. –

+0

Danke für diese wirklich klare Antwort! Ich habe diesen Code eigentlich nicht einmal benutzt, aber dieses merkwürdige Verhalten hat mich wirklich zum Nachdenken gebracht, also musste ich fragen :) Aber eine schnelle Frage: Wo finde ich die Standards? Ich habe viele Zitate gesehen, konnte sie aber immer noch nicht finden. – pedromanoel

0

Wie wäre es mit einem lokalen Zeiger auf ein 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); 
} 
+2

Ich glaube nicht, dass Sie eine Referenz verwenden möchten, wie Sie sind, oder? Es sieht so aus, als würdest du versuchen, 'current_set' neu zu setzen, und ich denke, du wirst am Ende' operator =() 'aufrufen, was' set_a' mit 'set_b' überschreibt. –

+0

Hoppla. Ich sollte wirklich lernen, indem ich Fragen stelle, anstatt zu versuchen, sie zu beantworten, denke ich. Bearbeitete Antwort, um Zeiger anstelle der Referenz zu verwenden ... wie ist das? –

2

Ich bin ziemlich überzeugt, dass Iteratoren aus verschiedenen Containern soll nicht auf diese Weise gemischt werden, und dass es in dem gefürchteten undefinierten Verhalten führt.

Also, was Sie sehen, ist reines Glück und Zufall, die auf die STL-Implementierung Ihres Compilers funktioniert, und in der aktuellen Phase des Mondes. Ich würde nicht darauf wetten, dass es irgendwo oder zu irgendeinem anderen Zeitpunkt funktioniert.

Verwandte Themen