Kann der Iterator eines Containers etwas anderes als einen Lvalue liefern?

8

Ich bin mehr oder weniger zu dem Schluss gekommen, dass es unmöglich ist, einen konformen Container zu schreiben, dessen value_type nicht direkt im Container gespeichert wurde. Ich denke, das ist bedauerlich, weil ich oft wünsche, ich hätte Container, in denen der Werttyp entweder teilweise berechnet oder aus nicht zusammenhängenden Stücken zusammengesetzt wird (Beispiele unten, aber nicht direkt relevant für die Frage). Ich weiß, wie man Iteratoren schreibt, die Proxy-Objekte benutzen, obwohl es ziemlich nervig ist. Aber ich frage mich jetzt, ob es wirklich einen Platz im C ++ - Standard für solche Bestien gibt. Es gibt wahrscheinlich zu viel Geschwafel hier; Die tl; dr-Version ist einfach: Was bedeuten die Absätze 1 und 6 von § 24.2.5 wirklich, und inwieweit verstößt die scheinbare Bedeutung gegen Standardalgorithmen? Oder anders ausgedrückt, wie können sie interpretiert werden, um Proxy-Iteratoren zuzulassen?

Pete Becker weist darauf hin, dass es wirklich nichts gibt, was meine Container dazu zwingt, die Anforderungen zu erfüllen, die für Standard-Library-Container gelten. Um jedoch einen Container mit vielen Standardalgorithmen zu verwenden, muss entweder ein konformer Iterator mit mindestens einem forward_iterator_tag vorhanden sein, oder es muss darüber liegen, aber immer noch die (wenn nicht formellen) betrieblichen Anforderungen des jeweiligen Algorithmus erfüllen auf seinen Iteratoren.

Hier ist meine Argumentation:

Zu Tabelle 96 (§ 23.2.1), Containeranforderungen, gehören:

%Vor%

Jetzt Vorwärts-Iterator:

  

§ 24.2.5, Abs. 1:

     

Eine Klasse oder ein Zeigertyp X erfüllt die Anforderungen eines Vorwärtsiterators, wenn ...

     

- Wenn X ein veränderbarer Iterator ist, ist reference eine Referenz auf T ; Wenn X ein konstanter Iterator ist, ist reference eine Referenz auf const T

Es stimmt, dass es keine direkte Anforderung für *a gibt, um reference zurückzugeben (wobei a vom Typ X ist). Die Anforderungen sind:

  

aus Tabelle 107 (Eingabe-Iteratoren) *a muss "in T konvertierbar sein", wenn a dereferenzierbar ist.

     

aus Tabelle 106 (Iteratoren) *r muss den Typ reference haben, wobei r vom Typ X& ist und dereferenziert werden kann.

Allerdings gibt Tabelle 106 auch an, dass ++r X& zurückgibt, also muss *++r reference sein. Außerdem muss (wie in Tabelle 107) *a++ reference sein, obwohl (Tabelle 109) a[n] nur "in Referenz umwandelbar" sein muss. Ich muss sagen, dass ich nicht sehe wie *a wo a ist vom Typ X und *r wo r vom Typ X& könnte anders sein, aber vielleicht fehlt mir etwas Subtilität.

Vielleicht gibt es hier einen kleinen Spielraum, aber nicht viel; Irgendwann müssen Sie darauf vorbereitet sein, ein T zu erstellen, wenn Sie noch keinen im Container haben, damit Sie einen Verweis darauf geben können.

Aber der Kicker ist

  

§ 24.2.5, Abs. 6 ( a und b sind Werte vom Typ X ):   Wenn a und b beide dereferenzierbar sind, dann a == b wenn und nur wenn *a und *b an dasselbe Objekt gebunden sind.

Ich kann keine formale Definition von bound to finden, aber es scheint mir, dass die übliche Strategie, um Iteratoren von nicht adressierbaren Objekten zu machen, ein Proxy-Objekt ist, das im Allgemeinen im Iterator selbst gespeichert ist. In diesem Fall würde es ein äußerst großzügiges Verständnis dessen erfordern, was "gebunden an" bedeutet, 24.2.5 / 6 anders zu interpretieren, als Gleichheitsvergleiche zwischen zwei verschiedenen Iteratorobjekten zu verbieten, selbst wenn sie die gleiche Position logisch anzeigen im Container.

Andererseits stelle ich fest, dass Dietmar Kühl, der wissen sollte, in seiner Antwort auf diese Frage sagt Folgendes:

  

C ++ 2011 hat entspannte Anforderungen und Iteratoren müssen nicht notwendigerweise einen lvalue

liefern

Kann also ein Iterator einen Proxy zurückgeben, oder nicht? Wenn ja, wie ist ein solcher Proxy beschaffen? Wo scheitere ich, dass ein solcher Iterator nicht konform ist?

Wie versprochen, werden einige Container, deren effektive value_types nicht zusammenhängend im Container gespeichert werden:

1) Ein kompakter assoziativer Container, dessen Schlüssel- und Werttypen effizienter in zwei separaten Vektoren gespeichert werden können. (Durch das Beibehalten der Schlüssel in einem Vektor kann auch die Cache-Benutzerfreundlichkeit verbessert und der Aufwand für die Zuweisung verringert werden.)

2) A vector<T> , das sich als map<integer_type, T> tarnt, was die Interoperabilität mit anderen map<X, T> -Typen vereinfacht.

3) Ein logischer Container, der durch Zippen mehrerer anderer Container gebildet wird und einen logischen value_type erzeugt, der tuple der Referenzen auf die Werttypen der gezippten Container darstellt. (In einigen Anwendungen können einer oder mehrere der komprimierten Container vollständig berechnet werden, entweder als Funktion der anderen Werte oder als Sequenznummer.)

4) Eine Ansicht eines Behälters eines Aggregattyps, der nur einige der Werte aufweist. (Möglicherweise sind sowohl der zugrunde liegende Container als auch die Ansicht Tupel, bei denen die Typliste des View-Tupels eine Teilmenge der zugrunde liegenden Containertypen ist, möglicherweise in einer anderen Reihenfolge.)

Ich bin sicher, dass andere Leute leicht zu dieser Liste hinzufügen können; Das sind nur die, die ich in den letzten Monaten auf irgendeine Art gehackt habe.

    
rici 13.12.2012, 23:50
quelle

2 Antworten

2

Beschränken Sie sich nicht, indem Sie an einen "konformen Behälter" denken: Es gibt nichts im Standard, das davon abhängt, einen zu haben. Stellen Sie sich Containeranforderungen als eine Kurzform vor, um die Anforderungen für die Container zu beschreiben, die im Standard definiert sind. Nichts mehr. Solange die Iteratoren, die Ihr Container erzeugt, gültig sind, sind Sie mit allen entsprechenden Algorithmen und vermutlich mit Algorithmen, die Sie selbst schreiben, einverstanden.

    
Pete Becker 13.12.2012 23:59
quelle
2

Das beste Modell ist std::vector< bool > . Es ist so nah wie möglich konform zu sein, aber seine Iteratoren ergeben Proxies.

Der Standard spezifiziert sogar, dass std::vector<bool>::reference eine Klasse ist. Die Tabelle der Containeranforderungen gibt jedoch an, dass X::reference "lvalue of T" ergibt. Also ist es streng nicht konform.

Aber die Iteratoren sind nicht an T gebunden. Der Iterator value_type muss T sein und die Anforderungen des Eingabe-Iterators berücksichtigen, reference muss in value_type konvertiert werden.

Wie Pete Becker erwähnt, sind die Anforderungstabellen ziemlich breit angelegt, und einzelne Algorithmen spezifizieren, was sie brauchen. Nur ein Algorithmus, der reference benötigt, um wirklich eine Referenz zu sein, wird brechen, was irgendwie nur das Offensichtliche angibt.

    
Potatoswatter 17.12.2012 06:10
quelle

Tags und Links