C++ std::set update ist mühsam: Ich kann ein Element nicht an Ort und Stelle ändern

Lesezeit: 6 Minuten

C stdset update ist muhsam Ich kann ein Element nicht
Figo

Ich finde den Update-Vorgang an std::set mühsam, da es keine solche API gibt cpReferenz. Also, was ich derzeit mache, ist ungefähr so:

//find element in set by iterator
Element copy = *iterator;
... // update member value on copy, varies
Set.erase(iterator);
Set.insert(copy);

Grundsätzlich kehrt der Iterator vorbei zurück Set ist ein const_iterator und Sie können seinen Wert nicht direkt ändern.

Gibt es einen besseren Weg, dies zu tun? Oder vielleicht sollte ich überschreiben std::set indem ich mein eigenes erstelle (von dem ich nicht genau weiß, wie es funktioniert..)

  • Erstellen Sie eine Inline-Funktion, wenn Sie der Meinung sind, dass die Verwendung von 2 Anweisungen bereits mühsam ist.

    – kennytm

    7. Februar 2010 um 19:02 Uhr

  • KennyTM hat den Nagel auf den Kopf getroffen. Es gibt keine Nachteile in Bezug auf die Leistung, also mach es einfach schon! 😛

    – j_random_hacker

    7. Februar 2010 um 19:24 Uhr

  • Wenn Sie eine Update-Funktion schreiben, möchten Sie diese vielleicht genauso modellieren wie Boost.MultiIndex: boost.org/doc/libs/release/libs/multi_index/doc/tutorial/…

    – Emil Cormier

    7. Februar 2010 um 21:55 Uhr

  • cplusplus.com ist eine schreckliche Referenz. Aspekte der Sprache “langweilig” zu finden, weil es … seltsam erscheint.

    – Leichtigkeitsrennen im Orbit

    12. Juni 2011 um 22:24 Uhr

  • Ich denke, der Nachteil dieses Ansatzes besteht darin, dass bei gleichzeitiger Verarbeitung eine Lese-/Schreibsperre aktiviert wird.

    – UchihaItachi-Inaktives-Konto

    31. Mai 2018 um 8:59 Uhr

C stdset update ist muhsam Ich kann ein Element nicht
Terry Mahaffey

set kehrt zurück const_iterators (Die Norm sagt set<T>::iterator ist constund das set<T>::const_iterator und set<T>::iterator kann tatsächlich derselbe Typ sein – siehe 23.2.4/6 in n3000.pdf), da es sich um einen bestellten Container handelt. Wenn es regelmäßig zurückkehrte iteratorkönnten Sie den Artikelwert unter dem Container ändern, wodurch möglicherweise die Reihenfolge geändert wird.

Ihre Lösung ist die idiomatische Art, Elemente in a zu ändern set.

  • Mitglieder von (nicht konstant) std::set kehrt nicht zurück const_iterator Und Sie kann Ändern Sie seine Elemente, wenn Sie vorsichtig sind. Warum erhält diese (falsche) Antwort so viele Upvotes? Was vermisse ich?

    – Avakar

    7. Februar 2010 um 19:36 Uhr


  • Meine Antwort ist richtig – deine ist falsch. Ich habe meinen Beitrag mit Verweisen auf den Standard aktualisiert.

    – Terry Mahaffey

    7. Februar 2010 um 19:43 Uhr

  • Terry, danke für die Diskussion. Ich habe nachgeprüft: Der Fehlerbericht wurde zwar 1998 eingereicht, aber nicht in C++03 eingebaut. Es wird in C++0x sein. Ihre Antwort ist zwar in Bezug auf den aktuellen Buchstaben der Norm nicht richtig, aber in Bezug auf die Absicht richtig. +1.

    – Avakar

    7. Februar 2010 um 20:02 Uhr


  • Danke für deinen Kommentar und deine Diskussion mit Avakar (oder Avatar?). Sie haben sehr geholfen.

    – Figo

    7. Februar 2010 um 20:28 Uhr

  • @avakar: Die Elemente sind technisch veränderbar, aber es ist nicht erlaubt, sie zu verändern. Dies ist ein Fehler im Standard.

    – Leichtigkeitsrennen im Orbit

    12. Juni 2011 um 22:25 Uhr

1646985009 10 C stdset update ist muhsam Ich kann ein Element nicht
Matthias M.

C++17 eingeführt extractsiehe Barrys Antwort.


Wenn Sie mit einer älteren Version festsitzen, gibt es im einfachen Fall 2 Möglichkeiten, dies zu tun:

  • Sie können verwenden mutable auf die Variable, die nicht Teil des Schlüssels sind
  • Sie können Ihre Klasse in a aufteilen Key Value Paar (und verwenden Sie a std::map)

Nun stellt sich die Frage nach dem kniffligen Fall: Was passiert eigentlich, wenn das Update das ändert key Teil des Objekts? Ihr Ansatz funktioniert, obwohl ich zugeben muss, dass es langweilig ist.

  • Das Hinzufügen von Mutable auf einem Nicht-Schlüsselmitglied ist möglicherweise in Ordnung, wenn es sich um eine interne / private Klasse handelt, aber es ist immer noch ein schmutziger Hack! Sobald die Klasse einigen Benutzern ausgesetzt ist, würde ich es niemals wagen, änderbar für Mitglieder zu verwenden, die nicht änderbar sein sollen! Das ist böse!

    – Martin Nito

    17. September 2015 um 16:00 Uhr


C stdset update ist muhsam Ich kann ein Element nicht
Barry

In C ++ 17 können Sie es besser machen mit extract()Dank an P0083:

// remove element from the set, but without needing
// to copy it or deallocate it
auto node = Set.extract(iterator);
// make changes to the value in place
node.value() = 42;
// reinsert it into the set, but again without needing 
// to copy or allocate
Set.insert(std::move(node));

Dies vermeidet eine zusätzliche Kopie Ihres Typs und eine zusätzliche Zuweisung/Aufhebung der Zuweisung und funktioniert auch mit Nur-Verschiebe-Typen.

Du kannst auch extract per Schlüssel. Wenn der Schlüssel fehlt, wird ein leerer Knoten zurückgegeben:

auto node = Set.extract(key);
if (node) // alternatively, !node.empty()
{
    node.value() = 42;
    Set.insert(std::move(node));
}

1646985010 491 C stdset update ist muhsam Ich kann ein Element nicht
Avakar

Aktualisieren: Obwohl das Folgende ab sofort zutrifft, wird das Verhalten als a Defekt und wird in der kommenden Version des Standards geändert. Wie sehr traurig.


Es gibt mehrere Punkte, die Ihre Frage ziemlich verwirrend machen.

  1. Funktionen können Werte zurückgeben, Klassen nicht. std::set ist eine Klasse und kann daher nichts zurückgeben.
  2. Wenn Sie anrufen können s.erase(iter)dann iter ist kein const_iterator. erase erfordert einen nicht konstanten Iterator.
  3. Alle Mitgliedsfunktionen von std::set die einen Iterator zurückgeben, geben einen nicht konstanten Iterator zurück, solange die Menge ebenfalls nicht konstant ist.

Sie dürfen den Wert eines Elements einer Menge ändern, solange die Aktualisierung die Reihenfolge der Elemente nicht ändert. Der folgende Code wird kompiliert und funktioniert einwandfrei.

#include <set>

int main()
{
    std::set<int> s;
    s.insert(10);
    s.insert(20);

    std::set<int>::iterator iter = s.find(20);

    // OK
    *iter = 30;

    // error, the following changes the order of elements
    // *iter = 0;
}

Wenn Ihr Update die Reihenfolge der Elemente ändert, müssen Sie sie löschen und erneut einfügen.

Vielleicht möchten Sie eine verwenden std::map stattdessen. Verwenden Sie den Teil von Element das wirkt sich auf die Bestellung des Schlüssels aus und legt alle ab Element als Wert. Es wird einige geringfügige Datenduplizierungen geben, aber Sie werden einfachere (und möglicherweise schnellere) Aktualisierungen haben.

1646985010 374 C stdset update ist muhsam Ich kann ein Element nicht
Bitmaske

Ich bin in C ++ 11 auf dasselbe Problem gestoßen, wo in der Tat ::std::set<T>::iterator ist konstant und erlaubt daher nicht, seinen Inhalt zu ändern, auch wenn wir wissen, dass die Transformation die nicht beeinflusst < unveränderlich. Sie können dies umgehen, indem Sie wickeln ::std::set in ein mutable_set Geben oder schreiben Sie einen Wrapper für den Inhalt:

  template <typename T>
  struct MutableWrapper {
    mutable T data;
    MutableWrapper(T const& data) : data(data) {}
    MutableWrapper(T&& data) : data(data) {}
    MutableWrapper const& operator=(T const& data) { this->data = data; }
    operator T&() const { return data; }
    T* operator->() const { return &data; }
    friend bool operator<(MutableWrapper const& a, MutableWrapper const& b) {
      return a.data < b.data;
    }   
    friend bool operator==(MutableWrapper const& a, MutableWrapper const& b) {
      return a.data == b.data;
    }   
    friend bool operator!=(MutableWrapper const& a, MutableWrapper const& b) {
      return a.data != b.data;
    }   
  };

Ich finde das viel einfacher und es funktioniert in 90% der Fälle, ohne dass der Benutzer überhaupt merkt, dass etwas zwischen dem Satz und dem tatsächlichen Typ liegt.

1646985011 794 C stdset update ist muhsam Ich kann ein Element nicht
Entwickler Null

Das geht in manchen Fällen schneller:

std::pair<std::set<int>::iterator, bool> result = Set.insert(value);
if (!result.second) {
  Set.erase(result.first);
  Set.insert(value);
}

Steht der Wert meist nicht schon in der std::set dann kann dies eine bessere Leistung haben.

990250cookie-checkC++ std::set update ist mühsam: Ich kann ein Element nicht an Ort und Stelle ändern

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy