Warum kann ich std :: array :: end () nicht dekrementieren?

8

Ich erstelle eine praktische display() -Funktionsvorlage für Containertypen. Die Ausgabe für das letzte Element unterscheidet sich vom Rest, also überprüfe ich, ob myIterator != --cont.cend(); . Dies funktioniert für std::vector , funktioniert aber nicht für std::array . Warum?

Hier ist ein MWE (nicht mein tatsächlicher Code):

%Vor%     
Jersey 10.01.2018, 12:44
quelle

4 Antworten

2

Es hängt davon ab, wie der Iterator definiert ist.

Es scheint, dass der Iterator für die Klassenvorlage std::array als Zeiger definiert ist. So fangen die Funktionen an, enden. cbegin, cend return nur den Zeiger. Wenn der Zeiger also als Wert zurückgegeben wird, darf er nicht verkleinert werden, weil lvalue erforderlich ist.

Für die Klassenvorlage std::vector ist der Iterator als eine benutzerdefinierte Klasse definiert, für die der Operator - () definiert ist.

Betrachten Sie das folgende Demonstrationsprogramm

%Vor%

Berücksichtigen Sie, dass Sie

schreiben können %Vor%

statt

%Vor%

vorausgesetzt, Sie fügen header <iterator> ein.

Sie können den Operator mit den umgekehrten Iteratoren der Klassenvorlage std::array verwenden, da der Standard sie explizit wie

definiert %Vor%

und die benutzerdefinierte Klasse std::reverse_iterator definiert operator --() .

Hier ist ein Demonstrationsprogramm

%Vor%     
Vlad from Moscow 10.01.2018, 12:54
quelle
13

Da dies , [expr.pre.increment] und [expr.post.increment] haben beide die Einschränkung, dass:

  

Der Operand soll ein veränderbarer Wert sein.

Nun sind weder vec.end() noch arr.end() lvalues, aber beide Typen sind implementierungsdefiniert (für Array und für Vektor ). In beiden Fällen würde ein einfacher Zeiger alle Iteratoranforderungen für diese Container erfüllen - und dies wäre ein Typ, der ein eingebautes Präfix und Postfix-Inkrement verwendet. In diesem Fall wäre --c.end() aufgrund der genannten Einschränkung schlecht ausgebildet. Wenn der Iteratortyp jedoch ein -Klassentyp ist, gilt die obige Einschränkung nicht - da wir nicht die eingebauten Inkrementoperatoren verwenden - und das Aufrufen von operator--() für eine Klasse hat diese Einschränkung nicht darauf (obwohl es könnte, wenn die Mitgliedsfunktion lvalue-reference-qualifiziert wäre).

So --c.end() für entweder Vektor oder Array funktioniert nicht garantiert, da, wenn end() einen Zeiger zurückgibt, dies nicht korrekt ist, und end() darf einen Zeiger zurückgeben. In Ihrer speziellen Implementierung hat vector 's iterator den Klassentyp, aber array ' s iterator ist nur ein Zeigertyp, weshalb der erste funktioniert, der zweite aber nicht.

Lieber std::prev(c.end()) , was für beide Arten von Containern für alle Implementierungen funktioniert.

    
Barry 10.01.2018 13:35
quelle
5

Reduziere niemals einen R-Wert, selbst wenn es kompiliert wird. Es ist für Leser des Codes nicht intuitiv.

Verwenden Sie stattdessen std::prev .

%Vor%     
Sebastian Redl 10.01.2018 13:11
quelle
3

Es ist eine Tatsache des Lebens aus dem C ++ - Standard.

--T.end() muss arbeiten, wenn T ein std::vector , ein std::list oder ein std::deque ist. Dies war bis einschließlich C ++ 11 korrekt; danach gab es Erleichterungen.

    
Bathsheba 10.01.2018 13:24
quelle