Ich habe an einem UTF-8-Iteratoradapter gearbeitet. Damit meine ich einen Adapter, der einen Iterator zu einer char
oder unsigned char
Sequenz in einen Iterator zu einer char32_t
Sequenz verwandelt . Meine Arbeit hier wurde von diesem Iterator inspiriert, den ich online gefunden habe .
Als ich jedoch den Standard durchgesehen habe, als ich mit meiner eigenen Implementierung begann, wurde mir klar: Es scheint nicht möglich zu sein, einen solchen Adapter zu implementieren, während er den Anforderungen entspricht, die C ++ an Iteratoren stellt.
Könnten Sie beispielsweise einen UTF-8-Iterator erstellen, der die InputIterator-Anforderungen erfüllt? Ja, aber nur solange der Iterator, den Sie erhalten , selbst kein InputIterator ist. Warum?
Weil InputIterator die Fähigkeit erfordert, denselben Iterator mehr als einmal zu dereferenzieren. Sie können auch mehrere Kopien dieses Iterators dereferenzieren, sofern alle gleich sind.
Das Dereferenzieren eines UTF-8-Iteratoradapters erfordert natürlich sowohl das Dereferenzieren als auch das potenzielle Inkrementieren des Basisiterators. Und wenn dieser Iterator ein InputIterator ist, können Sie den ursprünglichen Wert nicht zurückerhalten, nachdem Sie ihn erhöht haben. Und die Tatsache, dass Kopien funktionieren müssen, bedeutet, dass Sie keine lokal speichern können char32_t
, die den zuvor dekodierten Wert darstellt. Du hättest das tun können:
auto it = ...
auto it2 = it; //Copies an empty `char32_t`.
*it; //Accesses base iterator, storing `it.ch`.
*it; //Doesn't access the base iterator; simply returns `it.ch`.
*it2; //Cannot access `it.ch`, so must access base iterator.
OK, gut, Sie können also keine InputIterators verwenden. Aber was ist mit ForwardIterator? Ist es möglich, einen ForwardIterator-Adapter zu erstellen, der ForwardIterators über UTF-8-Zeichenfolgen anpassen kann?
Das ist auch problematisch, weil die Operation *it
ist erforderlich zu produzieren value_type&
oder const value_type&
. InputIterators können alles ausspucken, was konvertierbar ist value_type
, aber a ForwardIterator
ist erforderlich, um eine tatsächliche Referenz bereitzustellen [forward.iterators] /1.3:
Wenn
X
es sich um einen veränderlichen Iterator handelt,reference
wird auf verwiesenT
. WennX
es sich um einen konstanten Iterator handelt,reference
wird auf verwiesenconst T
Der einzige Rückgriff besteht darin, dass jeder solche Iterator ein mit sich herumträgt char32_t
, das nur existiert, um den Speicher für diese Referenz bereitzustellen. Und selbst dann muss dieser Wert jedes Mal aktualisiert werden, wenn die Iteratorinstanz inkrementiert und dereferenziert wird. Dies macht die alte Referenz effektiv ungültig, und der Standard erlaubt dies nicht explizit (eine Ungültigmachung kann nur erfolgen, wenn ein Iterator zerstört wird oder wenn der Container dies sagt).
Der oben genannte Code, den ich online gefunden habe, ist aus diesem Grund nicht gültig, da er eher einen uint32_t
(vor C ++ 11 geschriebenen) Wert als eine richtige Referenz zurückgibt .
Gibt es hier einen Rückgriff? Habe ich etwas im Standard übersehen oder eine Implementierungstechnik, mit der ich diese Probleme umgehen könnte? Oder ist dies mit dem aktuellen Wortlaut der Norm einfach nicht möglich?
Hinweis: Das Seltsame ist, dass es möglich zu sein scheint, einen konformen OutputIterator für die UTF-8-Konvertierung zu schreiben. Das heißt, ein Typ, der char32_t
UTF-8 in einen char
oder unsigned char
OutputIterator nimmt und schreibt .
ForwardIterator
nicht gut zu Proxy-Iteratoren passte , wie sievector<bool>
möglich waren. Es gab einen bekannten Artikel von Herb Sutter aus dem Jahr 1999 , in dem erklärt wurde, warum diese Entscheidung getroffen wurde. In der Neuzeit gab es einen Trend, dieses Thema zu überdenken. Ich finde einen von Eric Niebler . Es könnte mehr geben; Es könnte sogar einige geben, die Herb Sutter selbst in einigen C ++ - Vorschlägen geschrieben hat.Antworten:
Ich denke die kurze Antwort ist ja. Ein Iteratoradapter, der UTF-8 decodiert (und im Allgemeinen möglicherweise mehrere Eingabeelemente benötigt, um ein einzelnes Ausgabeelement zu erzeugen), muss auf einen Iterator gelegt werden, der (mindestens) BidirectionalIterator modelliert.
Beachten Sie, dass dies voraussetzt, dass Sie nur einen konstanten Iterator möchten (dh Sie lesen nur UTF-8 von der Eingabe, schreiben UTF-8 nicht in die zugrunde liegende Sammlung). Wenn Sie das Schreiben unterstützen möchten, werden die Dinge in Eile viel hässlicher. Wenn Sie auf UTF-32-Ebene von einem Wert zum anderen wechseln, kann dies leicht zu einer UTF-8-Codierung mit einer anderen Größe führen. Sie müssen also vorbereitet sein Einfügen / Löschen von Elementen in der Mitte der zugrunde liegenden Sammlung, wenn Sie das Schreiben unterstützen möchten.
quelle