Betrachten Sie das folgende Programm:
struct ghost
{
// ghosts like to pretend that they don't exist
ghost* operator&() const volatile { return 0; }
};
int main()
{
ghost clyde;
ghost* clydes_address = &clyde; // darn; that's not clyde's address :'(
}
Wie bekomme ich die clyde
Adresse?
Ich suche nach einer Lösung, die für alle Arten von Objekten gleich gut funktioniert. Eine C ++ 03-Lösung wäre schön, aber ich interessiere mich auch für C ++ 11-Lösungen. Vermeiden Sie nach Möglichkeit implementierungsspezifisches Verhalten.
Ich kenne die std::addressof
Funktionsvorlage von C ++ 11 , bin aber nicht daran interessiert, sie hier zu verwenden: Ich möchte verstehen, wie ein Standard Library-Implementierer diese Funktionsvorlage implementieren kann.
c++
c++11
operator-overloading
memory-address
James McNellis
quelle
quelle
:)
)CComPtr<>
undCComQIPtr<>
haben eine überladenoperator&
Antworten:
Update: In C ++ 11 kann man
std::addressof
anstelle von verwendenboost::addressof
.Kopieren wir zuerst den Code von Boost, abzüglich der Compiler-Umgehung von Bits:
Hinweis:
addressof
Kann nicht mit einem Zeiger auf die Funktion verwendet werdenWenn in C ++
void func();
deklariert ist,func
ist dies ein Verweis auf eine Funktion, die kein Argument akzeptiert und kein Ergebnis zurückgibt. Dieser Verweis auf eine Funktion kann trivial in einen Zeiger auf eine Funktion umgewandelt werden - von@Konstantin
: Nach 13.3.3.2 sind beideT &
undT *
für Funktionen nicht zu unterscheiden. Die erste ist eine Identitätskonvertierung und die zweite ist eine Funktion-zu-Zeiger-Konvertierung, die beide den Rang "Exakte Übereinstimmung" haben (13.3.3.1.1, Tabelle 9).Der Verweis auf die Funktion geht durch
addr_impl_ref
, es gibt eine Mehrdeutigkeit in der Überlastungsauflösung für die Auswahl vonf
, die dank des Dummy-Arguments gelöst wird, das eine Premiere0
istint
und zu einerlong
(Integral Conversion) befördert werden könnte.Wir geben also einfach den Zeiger zurück.
Wenn der Konvertierungsoperator a ergibt
T*
, haben wir eine Mehrdeutigkeit: Fürf(T&,long)
das zweite Argument ist eine integrale Promotion erforderlich, während fürf(T*,int)
den Konvertierungsoperator das erste aufgerufen wird (danke an @litb).Das ist , wenn
addr_impl_ref
Tritten an. Die C ++ Standard - Aufträge , daß eine Konvertierungssequenz höchstens einen benutzerdefinierten Umwandlung enthalten. Indemaddr_impl_ref
wir den Typ einschließen und die Verwendung einer Konvertierungssequenz bereits erzwingen, "deaktivieren" wir jeden Konvertierungsoperator, mit dem der Typ geliefert wird.Somit wird die
f(T&,long)
Überlastung ausgewählt (und die integrale Förderung durchgeführt).Somit wird die
f(T&,long)
Überladung ausgewählt, da dort der Typ nicht mit demT*
Parameter übereinstimmt .Hinweis: Aus den Anmerkungen in der Datei zur Borland-Kompatibilität geht hervor, dass Arrays nicht in Zeiger zerfallen, sondern als Referenz übergeben werden.
Wir möchten vermeiden
operator&
, dass der Typ angewendet wird, da er möglicherweise überladen ist.Der Standard garantiert, dass
reinterpret_cast
für diese Arbeit verwendet werden kann (siehe Antwort von @Matteo Italia: 5.2.10 / 10).Boost fügt einige Feinheiten mit
const
undvolatile
Qualifikationsmerkmalen hinzu, um Compiler-Warnungen zu vermeiden (und verwendet a ordnungsgemäßconst_cast
, um sie zu entfernen).T&
zuchar const volatile&
const
und ausvolatile
&
Operator an, um die Adresse zu übernehmenT*
Das
const
/volatile
Jonglieren ist ein bisschen schwarze Magie, aber es vereinfacht die Arbeit (anstatt 4 Überladungen bereitzustellen). Beachten Sie, dass, daT
unqualifiziert ist, wenn wir a bestehenghost const&
, dies der FallT*
istghost const*
, die Qualifikanten nicht wirklich verloren gegangen sind.BEARBEITEN: Die Zeigerüberladung wird für den Zeiger auf Funktionen verwendet, ich habe die obige Erklärung etwas geändert. Ich verstehe immer noch nicht, warum es notwendig ist .
Die folgende Ideone-Ausgabe fasst dies etwas zusammen.
quelle
f
Überladungen Funktionsvorlagen sind, während sie reguläre Mitgliedsfunktionen einer Vorlagenklasse sind, danke für den Hinweis. (Jetzt muss ich nur noch herausfinden, wozu die Überlastung gut ist, irgendein Tipp?)char*
". Danke, Matthieu.T*
? EDIT: Jetzt verstehe ich. Es würde, aber mit dem0
Argument würde es in einem Kreuz enden , wäre also mehrdeutig.Verwenden Sie
std::addressof
.Sie können sich vorstellen, hinter den Kulissen Folgendes zu tun:
Bestehende Implementierungen (einschließlich Boost.Addressof) tun genau das, indem sie sich nur um zusätzliche Pflege
const
undvolatile
Qualifizierung kümmern .quelle
Der Trick dahinter
boost::addressof
und die Implementierung von @Luc Danton beruhen auf der Magie desreinterpret_cast
; Der Standard besagt ausdrücklich in §5.2.10 ¶10, dassDies ermöglicht es uns nun, eine beliebige Objektreferenz in a zu konvertieren
char &
(mit einer Lebenslaufqualifikation, wenn die Referenz cv-qualifiziert ist), da jeder Zeiger in a konvertiert werden kann (möglicherweise cv-qualifiziert)char *
. Nachdem wir eine habenchar &
, ist die Überladung des Operators für das Objekt nicht mehr relevant, und wir können die Adresse mit dem eingebauten&
Operator abrufen .Die Boost-Implementierung fügt einige Schritte hinzu, um mit cv-qualifizierten Objekten zu arbeiten: Der erste Schritt
reinterpret_cast
wird ausgeführtconst volatile char &
, andernfalls würde eine einfachechar &
Besetzung fürconst
und / odervolatile
Referenzen nicht funktionieren (reinterpret_cast
kann nicht entfernt werdenconst
). Dann wird dasconst
undvolatile
mit entferntconst_cast
, die Adresse mit genommen&
und ein endgültigerreinterpet_cast
bis "korrekter" Typ erstellt.Das
const_cast
wird benötigt, um dasconst
/ zu entfernen, dasvolatile
nicht konstanten / flüchtigen Referenzen hinzugefügt werden könnte, aber es "schadet" nicht, was eineconst
/volatile
Referenz an erster Stelle war, da das Finalereinterpret_cast
die Lebenslaufqualifikation erneut hinzufügt, wenn dies der Fall ist dort an erster Stelle (reinterpret_cast
kann das nicht entfernenconst
, kann es aber hinzufügen).Was den Rest des Codes betrifftaddressof.hpp
, so scheint es, dass das meiste davon für Problemumgehungen ist. Dasstatic inline T * f( T * v, int )
scheint nur für den Borland-Compiler erforderlich zu sein, aber sein Vorhandensein macht es erforderlichaddr_impl_ref
, da sonst Zeigertypen von dieser zweiten Überladung erfasst würden.Bearbeiten : Die verschiedenen Überladungen haben unterschiedliche Funktionen, siehe @Matthieu M. ausgezeichnete Antwort .Nun, da bin ich mir auch nicht mehr sicher. Ich sollte diesen Code weiter untersuchen, aber jetzt koche ich das Abendessen :), ich werde es mir später ansehen.
quelle
void func();
boost::addressof(func);
. Das Entfernen der Überladung hindert gcc 4.3.4 jedoch nicht daran, den Code zu kompilieren und dieselbe Ausgabe zu erzeugen. Daher verstehe ich immer noch nicht, warum diese Überladung erforderlich ist .Ich habe eine Implementierung von
addressof
do this gesehen:Fragen Sie mich nicht, wie konform das ist!
quelle
char*
ist die aufgeführte Ausnahme zum Eingeben von Aliasing-Regeln.reinterpret_cast<char*>
gut definiert.[unsigned] char *
die Objektdarstellung des Objekts zu werfen und damit zu lesen. Dies ist ein weiterer Bereich, in demchar
besondere Privilegien bestehen.Schauen Sie sich boost :: addressof an und seine Implementierung an.
quelle
addressof
der Zeiger selbst zurückgegeben wird. Es ist fraglich, ob es das ist, was der Benutzer wollte oder nicht, aber es ist, wie es angegeben hat.addr_impl_ref
, daher sollte die Zeigerüberladung niemals aufgerufen werden ...