Ist COW basic_string
in C ++ 11 und höher verboten?
Hinsichtlich
” Bin ich richtig, dass C ++ 11 keine COW-basierten Implementierungen von zulässt std::string
?
Ja.
Hinsichtlich
“ Wenn ja, wird diese Einschränkung ausdrücklich irgendwo in dem neuen Standard angegeben (wo)?
Fast direkt durch Anforderungen konstanter Komplexität für eine Reihe von Operationen, die ein O ( n ) physisches Kopieren der Zeichenfolgendaten in einer COW-Implementierungerfordern würden.
Zum Beispiel für die Mitgliedsfunktionen
auto operator[](size_type pos) const -> const_reference;
auto operator[](size_type pos) -> reference;
… Was in einer COW-Implementierung ¹ sowohl das Kopieren von Zeichenfolgendaten auslösen würde, um die Freigabe des Zeichenfolgenwerts aufzuheben, erfordert der C ++ 11-Standard
C ++ 11 §21.4.5 / 4 :
“ Komplexität: konstante Zeit.
… Was ein solches Kopieren von Daten und damit COW ausschließt.
C ++ 03 unterstützt COW Implementierungen nicht diese ständigen Komplexitätsanforderungen haben, und durch unter bestimmten restriktiven Bedingungen, so dass Anrufe operator[]()
, at()
, begin()
, rbegin()
, end()
, oderrend()
ungültig zu machen , Referenzen, Zeiger und Iteratoren auf die Zeichenfolge Posten beziehen, dh auf möglicherweise incur a Kopieren von COW-Daten. Diese Unterstützung wurde in C ++ 11 entfernt.
Ist COW auch über die C ++ 11-Ungültigkeitsregeln verboten?
In einer anderen Antwort, die zum Zeitpunkt des Schreibens als Lösung ausgewählt wurde und die stark positiv bewertet und daher anscheinend geglaubt wird, wird dies behauptet
” Für eine COW-Zeichenfolge const
operator[]
würde das Aufrufen von non- das Erstellen einer Kopie (und das Ungültigmachen von Referenzen) erfordern, was im obigen [zitierten] Absatz [C ++ 11 §21.4.1 / 6] nicht zulässig ist. Daher ist es in C ++ 11 nicht mehr legal, eine COW-Zeichenfolge zu haben.
Diese Behauptung ist in zweierlei Hinsicht falsch und irreführend:
- Es zeigt fälschlicherweise an, dass nur die Nicht-
const
Item-Accessoren eine COW-Datenkopie auslösen müssen.
Aber auch die const
Elementzugriffsberechtigten müssen das Kopieren von Daten auslösen, da sie es dem Clientcode ermöglichen, Referenzen oder Zeiger zu bilden, die (in C ++ 11) später nicht über die Vorgänge ungültig gemacht werden dürfen, die das Kopieren von COW-Daten auslösen können.
- Es wird fälschlicherweise davon ausgegangen, dass das Kopieren von COW-Daten zu einer Ungültigmachung der Referenz führen kann.
Bei einer korrekten Implementierung erfolgt das Kopieren von COW-Daten, wobei die Freigabe des Zeichenfolgenwerts aufgehoben wird, zu einem Zeitpunkt, bevor Referenzen vorhanden sind, die ungültig gemacht werden können.
Um zu sehen, wie eine korrekte C ++ 11 COW-Implementierung von basic_string
funktionieren würde, wenn die O (1) -Anforderungen, die dies ungültig machen, ignoriert werden, stellen Sie sich eine Implementierung vor, bei der eine Zeichenfolge zwischen Eigentumsrichtlinien wechseln kann. Eine Zeichenfolgeninstanz beginnt mit der Richtlinie Sharable. Wenn diese Richtlinie aktiv ist, können keine externen Elementreferenzen vorhanden sein. Die Instanz kann zu einer eindeutigen Richtlinie übergehen und muss dies tun, wenn möglicherweise eine Elementreferenz erstellt wird, z. B. bei einem Aufruf von.c_str()
(zumindest, wenn dadurch ein Zeiger auf den internen Puffer erzeugt wird). Im allgemeinen Fall, dass mehrere Instanzen das Eigentum an dem Wert teilen, müssen die Zeichenfolgendaten kopiert werden. Nach diesem Übergang zur Richtlinie "Eindeutig" kann die Instanz nur durch einen Vorgang, der alle Verweise ungültig macht, z. B. die Zuweisung, wieder zu "Freigabefähig" zurückkehren.
Obwohl die Schlussfolgerung dieser Antwort, dass COW-Zeichenfolgen ausgeschlossen sind, richtig ist, ist die angebotene Argumentation falsch und stark irreführend.
Ich vermute, die Ursache für dieses Missverständnis ist ein nicht normativer Hinweis in Anhang C von C ++ 11:
C ++ 11 §C.2.11 [diff.cpp03.strings], ungefähr §21.3:
Änderung : basic_string
Anforderungen erlauben keine Zeichenfolgen mit Referenzzählung mehr.
Begründung: Die Invalidierung unterscheidet sich geringfügig mit Zeichenfolgen mit Referenzzählung. Diese Änderung reguliert das Verhalten für diesen internationalen Standard.
Auswirkung auf die ursprüngliche Funktion: Gültiger C ++ 2003-Code kann in dieser internationalen Norm anders ausgeführt werden
Hier erklärt die Begründung den Hauptgrund, warum man sich entschied, die spezielle COW-Unterstützung für C ++ 03 zu entfernen. Diese Begründung, das Warum , ist nicht, wie der Standard die COW-Implementierung effektiv verbietet. Der Standard verbietet COW über die O (1) -Anforderungen.
Kurz gesagt, die C ++ 11-Ungültigkeitsregeln schließen eine COW-Implementierung von nicht aus std::basic_string
. Sie schließen jedoch eine einigermaßen effiziente, uneingeschränkte COW-Implementierung im C ++ 03-Stil aus, wie sie in mindestens einer der Standardbibliotheksimplementierungen von g ++ enthalten ist. Die spezielle COW-Unterstützung für C ++ 03 ermöglichte eine praktische Effizienz, insbesondere unter Verwendung von const
Elementzugriffsmethoden, auf Kosten subtiler, komplexer Regeln für die Ungültigmachung:
C ++ 03 §21.3 / 5 mit COW-Unterstützung für den ersten Anruf:
” Verweise, Zeiger und Iteratoren, die sich auf die Elemente einer basic_string
Sequenz beziehen, können durch die folgenden Verwendungen dieses basic_string
Objekts ungültig gemacht werden :
- Als Argument für Nichtmitgliedsfunktionen swap()
(21.3.7.8), operator>>()
(21.3.7.9) und getline()
(21.3. 7.9).
- Als Argument für basic_string::swap()
.
- Anruf- data()
und c_str()
Mitgliedsfunktionen.
- Aufruf nicht const
Funktionen Mitglied, außer operator[]()
, at()
, begin()
, rbegin()
, end()
, und rend()
.
- Im Anschluss an einen der oben genannten Verwendungen außer den Formen insert()
und erase()
das Iteratoren zurück, der erste Anruf nicht const
Mitgliederfunktionen operator[]()
, at()
, begin()
, rbegin()
,end()
oder rend()
.
Diese Regeln sind so komplex und subtil, dass ich bezweifle, dass viele Programmierer, wenn überhaupt, eine genaue Zusammenfassung geben könnten. Ich könnte nicht.
Was ist, wenn O (1) -Anforderungen nicht berücksichtigt werden?
Wenn die konstanten operator[]
Zeitanforderungen für C ++ 11 für z. B. nicht berücksichtigt werden, ist COW for basic_string
möglicherweise technisch machbar, aber schwierig zu implementieren.
Zu den Vorgängen, die auf den Inhalt einer Zeichenfolge zugreifen können, ohne dass COW-Daten kopiert werden müssen, gehören:
- Verkettung über
+
.
- Ausgabe über
<<
.
- Verwenden eines
basic_string
als Argument für Standardbibliotheksfunktionen.
Letzteres, weil die Standardbibliothek sich auf implementierungsspezifische Kenntnisse und Konstrukte stützen darf.
Zusätzlich könnte eine Implementierung verschiedene nicht standardmäßige Funktionen für den Zugriff auf Zeichenfolgeninhalte bieten, ohne das Kopieren von COW-Daten auszulösen.
Ein Hauptkomplikationsfaktor ist, dass in C ++ 11 der Elementzugriff das basic_string
Kopieren von Daten auslösen muss (Aufheben der Freigabe der Zeichenfolgendaten), jedoch nicht ausgelöst werden muss , z. B. C ++ 11 §21.4.5 / 3 „ Throws: Nothing“. Daher kann keine normale dynamische Zuordnung verwendet werden, um einen neuen Puffer für das Kopieren von COW-Daten zu erstellen. Eine Möglichkeit, dies zu umgehen, besteht darin, einen speziellen Heap zu verwenden, in dem Speicher reserviert werden kann, ohne tatsächlich zugewiesen zu werden, und dann den erforderlichen Betrag für jede logische Referenz auf einen Zeichenfolgenwert zu reservieren . Das Reservieren und Aufheben der Reservierung in einem solchen Haufen kann eine konstante Zeit sein, O (1), und das Zuweisen des Betrags, den man bereits reserviert hat, kann seinnoexcept
. Um die Anforderungen des Standards zu erfüllen, scheint es bei diesem Ansatz einen solchen speziellen reservierungsbasierten Heap pro eindeutigem Allokator zu geben.
Hinweise:
¹ Der const
Elementzugriffsauslöser löst eine COW-Datenkopie aus, da der Clientcode einen Verweis oder Zeiger auf die Daten erhalten kann, die durch eine spätere Datenkopie, die beispielsweise vom Nicht- const
Artikelzugriffsgeber ausgelöst wird, nicht ungültig werden dürfen .