Dies ist eine Besonderheit in C ++. Obwohl wir Referenzen nicht als Typen betrachten, "sitzen" sie tatsächlich im Typensystem. Obwohl dies umständlich erscheint (da bei Verwendung von Referenzen die Referenzsemantik automatisch erfolgt und die Referenz "aus dem Weg geht"), gibt es einige vertretbare Gründe, warum Referenzen im Typsystem anstatt als separates Attribut außerhalb von modelliert werden Art.
Betrachten wir zunächst, dass nicht jedes Attribut eines deklarierten Namens im Typsystem enthalten sein muss. Aus der C-Sprache haben wir "Speicherklasse" und "Verknüpfung". Ein Name kann als eingeführt werden extern const int ri
, wobei dies die extern
statische Speicherklasse und das Vorhandensein einer Verknüpfung angibt. Der Typ ist einfach const int
.
C ++ umfasst offensichtlich die Vorstellung, dass Ausdrücke Attribute haben, die außerhalb des Typsystems liegen. Die Sprache hat jetzt ein Konzept der "Wertklasse", bei dem versucht wird, die wachsende Anzahl von Nicht-Typ-Attributen zu organisieren, die ein Ausdruck aufweisen kann.
Referenzen sind jedoch Typen. Warum?
Es wird verwendet , um in C ++ Anleitungen erklärt wird , dass eine Erklärung , wie const int &ri
eingeführt , ri
wie mit Typ const int
, aber Referenzsemantik. Diese Referenzsemantik war kein Typ; Es war einfach eine Art Attribut, das auf eine ungewöhnliche Beziehung zwischen dem Namen und dem Speicherort hinweist. Darüber hinaus wurde die Tatsache, dass Referenzen keine Typen sind, verwendet, um zu erklären, warum Sie keine Typen basierend auf Referenzen erstellen können, obwohl die Typkonstruktionssyntax dies zulässt. Zum Beispiel sind Arrays oder Zeiger auf Referenzen nicht möglich: const int &ari[5]
und const int &*pri
.
Tatsächlich sind Referenzen jedoch Typen und rufen so decltype(ri)
einen Knoten vom Referenztyp ab, der nicht qualifiziert ist. Sie müssen im Typbaum über diesen Knoten hinaus absteigen, um zum zugrunde liegenden Typ mit zu gelangen remove_reference
.
Wenn Sie verwenden ri
, wird die Referenz transparent aufgelöst, sodass ri
"aussieht und sich anfühlt i
" und als "Alias" dafür bezeichnet werden kann. Im Typsystem gibt es ri
jedoch tatsächlich einen Typ, der " Verweis auf const int
" ist.
Warum sind Referenztypen?
Wenn Referenzen keine Typen wären, würden diese Funktionen denselben Typ haben:
void foo(int);
void foo(int &);
Das kann einfach nicht aus Gründen sein, die ziemlich selbstverständlich sind. Wenn sie den gleichen Typ hätten, bedeutet dies, dass jede Deklaration für jede Definition geeignet wäre und daher jede (int)
Funktion verdächtigt werden müsste, eine Referenz zu nehmen.
Wenn Referenzen keine Typen wären, wären diese beiden Klassendeklarationen gleichwertig:
class foo {
int m;
};
class foo {
int &m;
};
Es wäre richtig, wenn eine Übersetzungseinheit eine Deklaration und eine andere Übersetzungseinheit im selben Programm die andere Deklaration verwenden würde.
Tatsache ist, dass eine Referenz einen Unterschied in der Implementierung impliziert und es unmöglich ist, diesen vom Typ zu trennen, da der Typ in C ++ mit der Implementierung einer Entität zu tun hat: ihr "Layout" in Bits sozusagen. Wenn zwei Funktionen denselben Typ haben, können sie mit denselben Konventionen für binäre Aufrufe aufgerufen werden: Der ABI ist derselbe. Wenn zwei Strukturen oder Klassen denselben Typ haben, ist ihr Layout und die Semantik des Zugriffs auf alle Mitglieder identisch. Das Vorhandensein von Referenzen ändert diese Aspekte von Typen. Daher ist es eine einfache Entwurfsentscheidung, sie in das Typsystem zu integrieren. (Beachten Sie hier jedoch ein Gegenargument: Es kann ein Struktur- / Klassenmitglied sein static
, das auch die Darstellung ändert; dies ist jedoch kein Typ!)
Daher sind Referenzen im Typensystem als "Bürger zweiter Klasse" (ähnlich wie Funktionen und Arrays in ISO C). Es gibt bestimmte Dinge, die wir mit Referenzen nicht "tun" können, z. B. das Deklarieren von Zeigern auf Referenzen oder Arrays davon. Das heißt aber nicht, dass sie keine Typen sind. Sie sind einfach keine Typen in einer Weise, die Sinn macht.
Nicht alle diese Einschränkungen zweiter Klasse sind wesentlich. Da es Referenzstrukturen gibt, kann es auch Referenzarrays geben! Z.B
// fantasy syntax
int x = 0, y = 0;
int &ar[2] = { x, y };
// ar[0] is now an alias for x: could be useful!
Dies ist einfach nicht in C ++ implementiert, das ist alles. Zeiger auf Referenzen sind jedoch überhaupt nicht sinnvoll, da ein aus einer Referenz entfernter Zeiger nur auf das referenzierte Objekt verweist. Der wahrscheinliche Grund, warum es keine Arrays von Referenzen gibt, ist, dass die C ++ - Leute Arrays als eine Art Low-Level-Feature betrachten, das von C geerbt wurde und in vielerlei Hinsicht fehlerhaft und irreparabel ist, und dass sie Arrays nicht als das berühren möchten Basis für etwas Neues. Das Vorhandensein von Referenzarrays wäre jedoch ein klares Beispiel dafür, wie Referenzen Typen sein müssen.
Nicht const
qualifizierbare Typen: auch in ISO C90 enthalten!
Einige Antworten deuten darauf hin, dass Referenzen kein const
Qualifikationsmerkmal haben. Das ist eher ein roter Hering, weil die Deklaration const int &ri = i
nicht einmal versucht , eine const
qualifizierte Referenz zu erstellen: Es ist eine Referenz auf einen const-qualifizierten Typ (was selbst nicht der Fall ist const
). Genau wie const in *ri
deklariert ein Zeiger auf etwas const
, aber dieser Zeiger ist selbst nicht const
.
Es ist jedoch richtig, dass Referenzen das const
Qualifikationsmerkmal nicht selbst tragen können.
Dies ist jedoch nicht so bizarr. Selbst in der Sprache ISO C 90 können nicht alle Typen sein const
. Arrays können nämlich nicht sein.
Erstens existiert die Syntax zum Deklarieren eines const-Arrays nicht: int a const [42]
ist fehlerhaft.
Was die obige Erklärung zu tun versucht, kann jedoch über ein Zwischenprodukt ausgedrückt werden typedef
:
typedef int array_t[42];
const array_t a;
Aber das macht nicht so, wie es aussieht. In dieser Erklärung ist es nicht , a
das bekommt const
qualifiziert, aber die Elemente! Das heißt, a[0]
ist ein const int
, ist aber a
nur "Array von int". Folglich ist hierfür keine Diagnose erforderlich:
int *p = a; /* surprise! */
Das macht:
a[0] = 1;
Dies unterstreicht erneut die Idee, dass Referenzen im Typsystem in gewisser Weise "zweite Klasse" sind, wie Arrays.
Beachten Sie, wie tief die Analogie gilt, da Arrays wie Referenzen auch ein "unsichtbares Konvertierungsverhalten" aufweisen. Ohne dass der Programmierer einen expliziten Operator verwenden muss, wird der Bezeichner a
automatisch zu einem int *
Zeiger, als ob der Ausdruck verwendet &a[0]
worden wäre. Dies ist analog dazu, wie eine Referenz ri
, wenn wir sie als primären Ausdruck verwenden, das Objekt, i
an das sie gebunden ist , auf magische Weise bezeichnet . Es ist nur ein weiterer "Zerfall" wie der "Zerfall von Array zu Zeiger".
Und so wie wir nicht durch den Zerfall von "Array zu Zeiger" in den falschen Gedanken verwirrt werden dürfen, dass "Arrays nur Zeiger in C und C ++ sind", dürfen wir auch nicht denken, dass Referenzen nur Aliase sind, die keinen eigenen Typ haben.
Wenn decltype(ri)
die übliche Konvertierung der Referenz in ihr Referenzobjekt sizeof a
unterdrückt wird, unterscheidet sich dies nicht wesentlich von der Unterdrückung der Array-zu-Zeiger-Konvertierung und der Bearbeitung des Array-Typs selbst zur Berechnung seiner Größe.
boolalpha(cout)
ist sehr ungewöhnlich. Sie könntenstd::cout << boolalpha
stattdessen tun .