Erlaubt C ++ 11 den Vektor <const T>?

81

Die Containeranforderungen wurden von C ++ 03 auf C ++ 11 geändert. Während C ++ 03 pauschale Anforderungen hatte (z. B. Kopierkonstruierbarkeit und Zuweisbarkeit für Vektoren), definiert C ++ 11 feinkörnige Anforderungen für jede Containeroperation (Abschnitt 23.2).

Infolgedessen können Sie beispielsweise einen Typ, der kopierkonstruierbar, aber nicht zuweisbar ist, wie z. B. eine Struktur mit einem const-Element, in einem Vektor speichern, solange Sie nur bestimmte Operationen ausführen, für die keine Zuweisung erforderlich ist (Konstruktion und push_backsolche Operationen) ; insertnicht ist).

Was ich mich frage ist: Bedeutet das, dass der Standard jetzt erlaubt vector<const T>? Ich sehe keinen Grund, warum dies nicht der Fall sein sollte - const Tgenau wie eine Struktur mit einem const-Member ist ein Typ, der kopierkonstruierbar, aber nicht zuweisbar ist -, aber ich habe möglicherweise etwas übersehen.

(Ein Teil von dem, was mich denken lässt, dass ich etwas verpasst habe, ist, dass der gcc-Trunk abstürzt und brennt, wenn Sie versuchen zu instanziieren vector<const T>, aber es ist in Ordnung, vector<T>wenn T ein const-Mitglied hat).

HighCommander4
quelle

Antworten:

54

Nein, ich glaube, die Allokatoranforderungen besagen, dass T ein "nicht konstanter Objekttyp ohne Referenz" sein kann.

Mit einem Vektor konstanter Objekte könnten Sie nicht viel anfangen. Und a const vector<T>wäre sowieso fast das gleiche.


Viele Jahre später scheint diese schnelle und schmutzige Antwort immer noch Kommentare und Stimmen anzulocken. Nicht immer auf. :-)

Um einige richtige Referenzen hinzuzufügen:

Für den C ++ 03-Standard, den ich auf Papier habe, heißt es in Tabelle 31 im Abschnitt [lib.allocator.requirements]:

T, U any type

Nicht, dass irgendein Typ tatsächlich funktioniert hätte.

Der nächste Standard, C ++ 11, sagt in einem engen Entwurf in [allocator.requirements] und jetzt in Tabelle 27:

T, U, C any non-const, non-reference object type

Das kommt dem sehr nahe, was ich oben aus dem Gedächtnis geschrieben habe. Darum ging es auch bei der Frage.

In C ++ 14 ( Entwurf N4296 ) heißt es in Tabelle 27 nun:

T, U, C any non-const object type

Möglicherweise, weil eine Referenz vielleicht doch kein Objekttyp ist?

In C ++ 17 ( Entwurf N4659 ) heißt es in Tabelle 30:

T, U, C any cv-unqualified object type (6.9)

Das ist also nicht nur constausgeschlossen, sondern auch volatile. Wahrscheinlich sowieso alte Nachrichten und nur eine Klarstellung.


Bitte beachten Sie auch die Informationen aus erster Hand von Howard Hinnant , die derzeit unten aufgeführt sind.

Bo Persson
quelle
42
Fazit: Wir haben keine Container für const T entworfen. Obwohl ich darüber nachgedacht habe. Und wir sind dem aus Versehen sehr nahe gekommen. Nach meinem besten Wissen ist der aktuelle Knackpunkt das Paar überladener addressElementfunktionen im Standardzuweiser: Wenn T const ist, haben diese beiden Überladungen dieselbe Signatur. Eine einfache Möglichkeit, dies zu korrigieren, besteht darin std::allocator<const T>, eine der Überlastungen zu spezialisieren und zu entfernen.
Howard Hinnant
4
@ HighCommander4: Ich bin nicht positiv. Unter libc ++ kann ich einen Vektor (auch einen nicht leeren) mit einem kooperativen Allokator erstellen. Ich kann nichts anderes damit machen (non-const). Ich bin mir nicht sicher, ob das zu Ihrer Definition von "funktioniert" passt. Ich bin auch nicht positiv, wenn ich unabsichtlich eine Erweiterung ausnutze. Um sicher zu gehen, müsste ich viel mehr Zeit in diese Frage investieren. Ich habe schon früher eine solche Investition getätigt, aber das war vor einigen Jahren, und in der Zwischenzeit haben sich viele Dinge geändert. Wenn es funktioniert, ist es nicht beabsichtigt vom Ausschuss.
Howard Hinnant
1
@ Howard: Ich kann mir kein technisches Hindernis vorstellen, um dies zu tun push_back. Aber wenn es nicht beabsichtigt ist, ist es besser, wenn wir es nicht tun. Ich war nur neugierig.
HighCommander4
7
Ein Cache ist oft ein veränderlicher Container unveränderlicher Objekte, und ein sortierter Vektor ist oft eine Alternative zu einer Karte. Daher bin ich nicht der Meinung, dass ein Vektor von const-Objekten von geringem Nutzen ist.
Chris Oldwood
9
Es ist Schande. Ich habe std::vector<const T>genau verwendet, weil es sehr ähnlich ist const std::vector<T>, aber ohne die negativen Auswirkungen des letzteren auf die Klasse, die es hält. In der Tat std::vector<const T>ist genau das, was ich semantisch in den meisten Fällen brauche, wo ich benutze vector. Jetzt muss ich fallen const- zusammen mit der Zuverlässigkeit, die es bietet.
Violette Giraffe
29

Aktualisieren

Unter der akzeptierten (und richtigen) Antwort habe ich 2011 kommentiert:

Fazit: Wir haben keine Container für die Aufbewahrung entworfen const T. Obwohl ich darüber nachgedacht habe. Und wir sind dem aus Versehen sehr nahe gekommen. Nach meinem besten Wissen ist der aktuelle Knackpunkt das Paar überladener addressElementfunktionen im Standardzuweiser: Wann Tist const, haben diese beiden Überladungen dieselbe Signatur. Eine einfache Möglichkeit, dies zu korrigieren, besteht darin std::allocator<const T>, eine der Überlastungen zu spezialisieren und zu entfernen.

Mit dem bevorstehenden C ++ 17-Entwurf scheint es mir, dass wir jetzt legalisiert haben vector<const T>, und ich glaube auch, dass wir es versehentlich getan haben . :-)

P0174R0 entfernt die addressÜberlastungen von std::allocator<T>. P0174R0 erwähnt die Unterstützung nicht std::allocator<const T>als Teil seiner Begründung.

Korrektur

In den Kommentaren unten stellt TC korrekt fest, dass die addressÜberladungen veraltet und nicht entfernt sind. Mein Fehler. Die veralteten Mitglieder erscheinen nicht in 20.10.9, wo das std::allocatordefiniert ist, sondern werden stattdessen in Abschnitt D.9 verbannt. Ich habe es versäumt, Kapitel D nach dieser Möglichkeit zu durchsuchen, als ich dies gepostet habe.

Danke TC für die Korrektur. Ich habe darüber nachgedacht, diese irreführende Antwort zu löschen, aber vielleicht ist es am besten, sie mit dieser Korrektur zu belassen, damit möglicherweise jemand anderes die Spezifikation nicht so falsch liest, wie ich es getan habe.

Howard Hinnant
quelle
6
Das ist ziemlich amüsant! (Jetzt müssen wir nur noch sehr leise sein und es in C ++ 17
einbinden
Verbietet die Allokator-Anforderungstabelle sie nicht immer noch vollständig? Unabhängig davon wird P0174R2 (die Revision, in der abgestimmt wurde) nur abgelehnt, nicht entfernt address.
TC
@TC: Du hast absolut recht. Danke für die Korrektur.
Howard Hinnant
Also wird c ++ 2x endlich erlauben vector<const T>:)
MM
1
Die Antwort "Fazit: Wir haben keine Container für const T entworfen" setzt voraus, dass das Ziel darin besteht, dass der Container "const T" enthält. Man könnte jedoch argumentieren, dass das Benutzerziel darin besteht, Operationen auf dem Container einzuschränken - so dass z. B. 'back ()' "const T &" zurückgibt - unabhängig davon, was der Container enthält.
Hans Olsson
8

Obwohl wir bereits sehr gute Antworten darauf haben, habe ich beschlossen, mit einer praktischeren Antwort beizutragen, um zu zeigen, was getan werden kann und was nicht.

Das funktioniert also nicht:

vector<const T> vec; 

Lesen Sie einfach die anderen Antworten, um zu verstehen, warum. Und wie Sie vielleicht vermutet haben, funktioniert dies auch nicht:

vector<const shared_ptr<T>> vec;

Tist nicht mehr const, sondern vectorhält shared_ptrs, nicht Ts.

Auf der anderen Seite, dies tut Arbeit:

vector<const T *> vec;
vector<T const *> vec;  // the same as above

In diesem Fall ist const jedoch das Objekt, auf das gezeigt wird, nicht der Zeiger selbst (was der Vektor speichert). Dies wäre gleichbedeutend mit:

vector<shared_ptr<const T>> vec;

Welches ist in Ordnung.

Wenn wir jedoch constdas Ende des Ausdrucks einfügen, wird der Zeiger jetzt in a umgewandelt const, sodass Folgendes nicht kompiliert wird:

vector<T * const> vec;

Ein bisschen verwirrend, stimme ich zu, aber man gewöhnt sich daran.

Lucio Paiva
quelle
4

Ergänzend zu den anderen Antworten besteht ein anderer Ansatz darin, Folgendes zu verwenden:

vector<unique_ptr<const T>> vec;

Wenn Sie dies durchsetzen möchten, hat dies nur vecdas Eigentum an den Elementen. Oder wenn Sie eine Dynamik zum Verschieben vecund Verschieben von Objekten wünschen .

Wie erwähnt, Zeiger constkann Semantik verwirrend, aber shared_ptrund unique_ptrist es nicht. const unique_ptr<T>ist ein const-Zeiger und unique_ptr<const T>ein const-Zeiger, wie Sie es erwarten würden.

Daniel Gouvêa
quelle