webentwicklung-frage-antwort-db.com.de

Legalität der Implementierung von COW std :: string in C ++ 11

Ich hatte verstanden, dass Copy-on-Write keine praktikable Möglichkeit ist, ein konformes std::string In C++ 11 zu implementieren, aber als es kürzlich in der Diskussion auftauchte, war ich nicht in der Lage, diese Aussage direkt zu unterstützen.

Stimmt es, dass C++ 11 keine COW-basierten Implementierungen von std::string Zulässt?

Wenn ja, ist diese Einschränkung irgendwo im neuen Standard explizit angegeben (wo)?

Oder ist diese Einschränkung impliziert, in dem Sinne, dass es der kombinierte Effekt der neuen Anforderungen an std::string Ist, der eine COW-basierte Implementierung von std::string Ausschließt. In diesem Fall wäre ich an einer Ableitung von Kapiteln und Versen von 'C++ 11 verbietet effektiv COW-basierte std::string - Implementierungen' interessiert.

112
acm

Dies ist nicht zulässig, da gemäß dem Standard 21.4.1 p6 die Ungültigmachung von Iteratoren/Referenzen nur zulässig ist

- als Argument für jede Standardbibliotheksfunktion, die einen Verweis auf non-const basic_string als Argument verwendet.

- Aufrufen von Nicht-Konstanten-Member-Funktionen, außer Operator [], at, front, back, begin, rbegin, end und rend.

Für eine COW-Zeichenfolge muss zum Aufrufen von non-const operator[] Eine Kopie erstellt (und Referenzen ungültig gemacht) werden, was im obigen Absatz nicht zulässig ist. Daher ist es nicht mehr legal, eine COW-Zeichenfolge in C++ 11 zu haben.

115
Dave S

Die Antworten von Dave S und gbjbaanb sind richtig . (Und Luc Danton's ist auch richtig, obwohl es eher eine Nebenwirkung des Verbots von COW-Zeichenfolgen ist als die ursprüngliche Regel, die es verbietet.)

Aber um etwas Verwirrung zu beseitigen, werde ich eine weitere Erläuterung hinzufügen. Verschiedene Kommentare verlinken auf ein Kommentar von mir zum GCC-Bugzilla , der das folgende Beispiel enthält:

std::string s("str");
const char* p = s.data();
{
    std::string s2(s);
    (void) s[0];
}
std::cout << *p << '\n';  // p is dangling

In diesem Beispiel soll gezeigt werden, warum die COW-Zeichenfolge (Reference Counted) von GCC in C++ 11 nicht gültig ist. Der C++ 11-Standard erfordert, dass dieser Code ordnungsgemäß funktioniert. Nichts im Code erlaubt das Ungültigmachen von p in C++ 11.

Unter Verwendung der alten referenzzählenden Implementierung von GCC std::string Hat dieser Code ein undefiniertes Verhalten, da p ist ungültig ist und zu einem baumelnden Zeiger wird. (Wenn s2 Erstellt wird, werden die Daten mit s geteilt, aber um eine nicht konstante Referenz über s[0] Zu erhalten, müssen die Daten freigegeben werden. s führt eine "Kopie beim Schreiben" durch, da die Referenz s[0] möglicherweise zum Schreiben in s verwendet werden kann. Dann verlässt s2 den Gültigkeitsbereich und zerstört das Array, auf das verwiesen wird bis von p).

Der C++ 03-Standard lässt dieses Verhalten in 21.3 [lib.basic.string] p5 explizit zu, wo dies nach einem Aufruf von data() Der erste Aufruf von operator[]() kann Zeiger, Referenzen und Iteratoren ungültig machen. Der COW-String von GCC war also eine gültige C++ 03-Implementierung.

Der C++ 11-Standard lässt dieses Verhalten nicht mehr zu , da kein Aufruf von operator[]() unabhängig davon Zeiger, Referenzen oder Iteratoren ungültig machen kann ob sie einem Aufruf von data() folgen.

Das obige Beispiel muss in C++ 11 funktionieren, aber nicht mit der Art von COW-Zeichenfolge von libstdc ++ arbeiten, daher ist diese Art von COW-Zeichenfolge in C++ 11 nicht zulässig.

43
Jonathan Wakely

Es ist, dass CoW ein akzeptabler Mechanismus ist, um schnellere Saiten herzustellen ... aber ...

dies verlangsamt Multithreading-Code (all das Sperren, um zu prüfen, ob Sie der einzige sind, der Leistungseinbußen bei der Verwendung vieler Zeichenfolgen begeht). Dies war der Hauptgrund, warum CoW vor Jahren getötet wurde.

Die anderen Gründe sind, dass der Operator [] Ihnen die Zeichenfolgendaten zurückgibt, ohne dass Sie den Schutz haben, eine Zeichenfolge zu überschreiben, von der jemand anderes erwartet, dass sie sich nicht ändert. Gleiches gilt für c_str() und data().

Quick google sagt, dass das Multithreading im Grunde der Grund ist, warum es effektiv nicht zugelassen wurde (nicht explizit).

In dem Vorschlag heißt es:

Vorschlag

Wir schlagen vor, alle Iterator- und Elementzugriffsoperationen gleichzeitig sicher ausführbar zu machen.

Wir erhöhen die Stabilität von Operationen auch im sequentiellen Code.

Diese Änderung verhindert effektiv das Kopieren beim Schreiben.

gefolgt von

Der größte potenzielle Leistungsverlust aufgrund einer Abkehr von Copy-on-Write-Implementierungen ist der erhöhte Speicherverbrauch für Anwendungen mit sehr großen, meistens lesenden Zeichenfolgen. Wir sind jedoch der Meinung, dass Seile für diese Anwendungen eine bessere technische Lösung darstellen, und empfehlen, dass ein Seilvorschlag für die Aufnahme in die Bibliothek TR2 in Betracht gezogen wird.

Seile sind Teil von STLPort und SGIs STL.

19
gbjbaanb

Ab 21.4.2 basic_string-Konstruktoren und Zuweisungsoperatoren [string.cons]

basic_string(const basic_string<charT,traits,Allocator>& str);

[...]

2 Effekte: Konstruiert ein Objekt der Klasse basic_string Wie in Tabelle 64 angegeben. [...]

Tabelle 64 dokumentiert hilfreich, dass this->data() nach der Erstellung eines Objekts über diesen (Kopier-) Konstruktor den folgenden Wert hat:

zeigt auf das erste Element einer zugewiesenen Kopie des Arrays, auf dessen erstes Element str.data () zeigt

Es gibt ähnliche Anforderungen für andere ähnliche Konstruktoren.

5
Luc Danton

Da jetzt garantiert ist, dass Zeichenfolgen zusammenhängend gespeichert werden und Sie jetzt einen Zeiger auf den internen Speicher einer Zeichenfolge nehmen können (dh & str [0] funktioniert wie bei einem Array), ist es nicht möglich, eine nützliche COW zu erstellen Implementierung. Sie müssten eine Kopie für viel zu viele Dinge machen. Selbst wenn Sie nur operator[] Oder begin() für einen Nicht-Konstanten-String verwenden, ist eine Kopie erforderlich.

1
Dirk Holsopple