C ++ - Standardcontainer und -zuordner stellen typedefs für den vom Container verwendeten Zeigertyp bereit, d. h .:
%Vor% Der tatsächliche Zeigertyp, der zum Erstellen des typedef verwendet wird, wird über std::allocator_traits
Da jeder Container auch ein value_type
typedef hat, ist der Zweck von pointer
typedef vermutlich für eine seltsame Situation, in der der verwendete Zeigertyp etwas anderes als value_type*
ist. Ich habe nie persönlich einen Anwendungsfall für so etwas gesehen, aber ich nehme an, das Normenkomitee wollte die Möglichkeit bieten, benutzerdefinierte Pointer-Typen mit Containern zu verwenden.
Das Problem ist, dass dies mit den Definitionen für die Funktionen in std::allocator_traits
nicht übereinstimmt. Insbesondere haben wir in std::allocator_traits
die Funktion construct
, die wie folgt definiert ist:
... welches nur a.construct(p, std::forward<Args>(args)...)
Beachten Sie jedoch, dass diese Funktion keinen benutzerdefinierten Zeigertyp vorsieht. Der Parameter p
ist ein einfacher nativer Zeiger.
Also, warum ist die Definition dieser Funktion nicht ungefähr wie folgt:
%Vor% Es sieht so aus, als ob Container, die std::allocator_traits<Alloc>::construct
verwendeten, fehlschlagen würden, wenn sie mit einem Allocator verwendet würden, der einen benutzerdefinierten Zeigertyp definiert.
Also, was ist hier los? Oder missverstehe ich den Zweck von pointer
typedefs an erster Stelle?
Diese Dichotomie ist zielgerichtet und stellt kein Problem dar. Die construct
-Memberfunktion wird typischerweise wie folgt implementiert:
i.e. Es wird an das Placement new
weitergeleitet, das wiederum diese Signatur hat:
Sie benötigen also letztendlich einen "echten" Zeiger, um die Platzierung neu anzulegen. Um den Typ des Objekts, das konstruiert werden soll, zu vermitteln, ist es nur sinnvoll, diese Information im Zeigertyp (z. B. U*
) mitzuliefern.
Für die Symmetrie wird destroy
auch in Form eines tatsächlichen Zeigers formuliert und normalerweise wie folgt implementiert:
Der Hauptanwendungsfall für den "extravaganten Zeiger" ist das Platzieren von Objekten in einem gemeinsamen Speicher. Eine Klasse mit dem Namen offset_ptr
wird normalerweise für diesen Zweck verwendet, und ein Zuordner kann erstellt werden, um Speicher, auf den von offset_ptr
verwiesen wird, zuzuweisen und freizugeben. Und somit funktioniert der allocator_traits
und allocator
allocate
und deallocate
Traffic in pointer
anstelle von value_type*
.
Es stellt sich also die Frage: Wenn du pointer
hast und T*
brauchst, was machst du?
Ich kenne zwei Techniken, um ein T*
von einem pointer p
1.
std::addressof(*p);
Wenn Sie eine pointer p
dereferenzieren, muss dies zu einem lvalue gemäß dem Standard führen. Es wäre jedoch nett, diese Anforderung lockern zu können (z. B. in Betracht ziehen, dass pointer
eine Proxy-Referenz wie vector<bool>::reference
zurückgibt). std::addressof
wird angegeben, um T*
an einen beliebigen lvalue zurückzugeben:
2.
to_raw_pointer(p); // where:
Dies ruft pointer
's operator->()
auf, die entweder direkt ein T*
zurückgeben oder auf etwas weiterleiten, das entweder direkt oder indirekt ein T*
zurückgibt. Alle pointer
types sollten operator->()
unterstützen, auch wenn auf bool
verwiesen wird. Ein Nachteil dieser Technik ist, dass derzeit operator->()
nicht aufgerufen werden muss, es sei denn die pointer
ist dereferenzierbar. Diese Einschränkung sollte in der Norm aufgehoben werden.
In C ++ 14 kann der Rückgabetyp der zweiten Überladung (also eigentlich beide Überladungen), bequem durch auto
ersetzt werden.
Wenn Sie T*
haben und ein pointer
erstellen möchten, haben Sie kein Glück. Es gibt keinen portablen Weg, um in diese Richtung zu konvertieren.
Beachten Sie auch dieses tangential verwandte LWG-Problem zum Rückgabetyp vector::data()
member function. Er ist zwischen value_type*
, pointer
und zurück aufgetaucht und ist derzeit (und absichtlich) value_type*
.