Wir wissen, dass eine "const-Variable" angibt, dass Sie die Variable nach ihrer Zuweisung nicht wie folgt ändern können:
int const i = 1;
i = 2;
Das obige Programm kann nicht kompiliert werden. gcc fordert mit einem Fehler auf:
assignment of read-only variable 'i'
Kein Problem, ich kann es verstehen, aber das folgende Beispiel ist für mich unverständlich:
#include<iostream>
using namespace std;
int main()
{
boolalpha(cout);
int const i = 1;
cout << is_const<decltype(i)>::value << endl;
int const &ri = i;
cout << is_const<decltype(ri)>::value << endl;
return 0;
}
Es gibt aus
true
false
Seltsam. Wir wissen, dass wir, sobald eine Referenz an einen Namen / eine Variable gebunden ist, diese Bindung nicht mehr ändern können, sondern ihr gebundenes Objekt. Ich nehme an, der Typ ri
sollte der gleiche sein wie i
: Wann i
ist ein int const
, warum ri
nicht const
?
boolalpha(cout)
ist sehr ungewöhnlich. Sie könntenstd::cout << boolalpha
stattdessen tun .ri
, ein "Alias" zu sein, von dem man nicht unterscheiden kanni
.i
ist auch eine Referenz, aber aus historischen Gründen deklarieren Sie sie nicht explizit als solche. Diesi
ist also eine Referenz, die sich auf einen Speicher bezieht, undri
eine Referenz, die sich auf denselben Speicher bezieht. Aber es gibt keinen Unterschied in der Natur zwischeni
undri
. Da Sie die Bindung einer Referenz nicht ändern können, müssen Sie sie nicht als qualifizierenconst
. Und lassen Sie mich behaupten, dass der @ Kaz-Kommentar viel besser ist als die validierte Antwort (erklären Sie Referenzen niemals mit Zeigern, ein ref ist ein Name, ein ptr ist eine Variable).is_const
, auchtrue
in diesem Fall zurückzukehren. Meiner Meinung nach ist dies ein gutes Beispiel dafür, warumconst
es grundsätzlich rückwärts geht. Ein "veränderliches" Attribut (a la Rustmut
) scheint konsistenter zu sein.is_const<int const &>::value
falsch?" Geändert werden. o.ä; Ich kämpfe darum, eine andere Bedeutung für die Frage zu erkennen, als nach dem Verhalten von Typmerkmalen zu fragenAntworten:
Dies mag kontraintuitiv erscheinen, aber ich denke, der Weg, dies zu verstehen, besteht darin, zu erkennen, dass Referenzen in gewisser Hinsicht syntaktisch wie Zeiger behandelt werden .
Dies scheint für einen Zeiger logisch :
Ausgabe:
Dies ist logisch, da wir wissen, dass es nicht das Zeigerobjekt ist, das const ist (es kann dazu gebracht werden, auf eine andere Stelle zu zeigen), sondern das Objekt, auf das gezeigt wird.
Wir sehen also die Konstanz des Zeigers selbst korrekt als zurückgegeben
false
.Wenn wir den Zeiger selbst machen wollen, müssen
const
wir sagen:Ausgabe:
Ich denke, wir sehen eine syntaktische Analogie mit der Referenz .
Jedoch Referenzen semantisch anders Zeiger besonders in sind ein entscheidender Hinsicht sind wir nicht erlaubt zu binden einen Verweis auf ein anderes Objekt einmal gebunden.
Obwohl Referenzen dieselbe Syntax wie Zeiger haben, sind die Regeln unterschiedlich und die Sprache hindert uns daran, die Referenz selbst
const
wie folgt zu deklarieren :Ich gehe davon aus, dass wir dies nicht dürfen, da dies nicht erforderlich zu sein scheint, wenn die Sprachregeln verhindern, dass die Referenz auf die gleiche Weise wie ein Zeiger zurückgebunden wird (wenn sie nicht deklariert ist
const
).Um die Frage zu beantworten:
In Ihrem Beispiel bewirkt die Syntax, dass auf das Objekt
const
genauso verwiesen wird, wie wenn Sie einen Zeiger deklarieren würden .Zu Recht oder zu Unrecht dürfen wir die Referenz nicht selbst machen,
const
aber wenn wir es wären, würde es so aussehen:Warum wird das
decltype()
nicht auf das Objekt übertragen, an das der Schiedsrichter gebunden ist?Ich nehme an, dies dient der semantischen Äquivalenz mit Zeigern, und möglicherweise besteht die Funktion von
decltype()
(deklarierter Typ) darin, auf das zurückzublicken, was vor der Bindung deklariert wurde .quelle
decltype
, und stellte fest, dass dies nicht der Fall war .const
,std::is_const
muss also zurückkehrenfalse
. Sie hätten stattdessen Formulierungen verwenden können, die bedeuteten, dass sie zurückkehren müssentrue
, aber sie taten es nicht. Das ist es! All dieses Zeug über Zeiger, "Ich nehme an", "Ich nehme an" usw. liefert keine wirkliche Erklärung.decltype
es sich nicht um eine Laufzeitoperation handelt. Daher trifft die Idee "Zur Laufzeit verhalten sich Referenzen wie Zeiger", ob korrekt oder nicht, nicht wirklich zu.std::is_const
prüft, ob der Typ const-qualifiziert ist oder nicht.Die Referenz kann jedoch nicht const-qualifiziert werden. Referenzen [dcl.ref] / 1
So
is_const<decltype(ri)>::value
kehrtfalse
becuaseri
(die Referenz) ist kein const qualifizierten Typ. Wie Sie sagten, können wir eine Referenz nach der Initialisierung nicht erneut binden, was bedeutet, dass die Referenz immer "const" ist. Andererseits ist eine const-qualifizierte Referenz oder eine const-unqualifizierte Referenz möglicherweise nicht sinnvoll.quelle
is_const
zurückgegebentrue
? Diese Antwort versucht, eine Analogie zu ziehen, wie Zeiger optional wieder eingesetzt werden können, wohingegen Referenzen dies nicht tun - und führt dabei aus demselben Grund zu Selbstwiderspruch. Ich bin mir nicht sicher, ob es eine echte Erklärung gibt, außer einer etwas willkürlichen Entscheidung derjenigen, die den Standard schreiben, und manchmal ist das das Beste, auf das wir hoffen können. Daher diese Antwort.decltype
ist keine Funktion und daher direkt auf den Nachschlagewerken selbst und nicht auf die genannten zu Objekt. (Dies ist vielleicht relevanter für die Antwort "Referenzen sind im Grunde Zeiger", aber ich denke immer noch, dass es Teil dessen ist, was dieses Beispiel verwirrend und daher erwähnenswert macht.)decltype(name)
anders als ein Generaldecltype(expr)
. So ist zum Beispieldecltype(i)
der deklarierte Typ, deri
istconst int
, währenddecltype((i))
wäreint const &
.Sie müssen verwenden,
std::remove_reference
um den Wert zu erhalten, den Sie suchen.Weitere Informationen finden Sie in diesem Beitrag .
quelle
Warum sind Makros nicht
const
? Funktionen? Literale? Die Namen der Typen?const
Dinge sind nur eine Teilmenge unveränderlicher Dinge.Da Referenztypen genau das sind - Typen -, mag es sinnvoll gewesen sein, den
const
Qualifizierer für alle für die Symmetrie mit anderen Typen (insbesondere mit Zeigertypen) zu benötigen , aber dies würde sehr schnell sehr mühsam werden.Wenn C ++ standardmäßig unveränderliche Objekte hätte und das
mutable
Schlüsselwort für alles benötigt, was Sie nicht sein wolltenconst
, wäre dies einfach gewesen: Erlauben Sie Programmierern einfach nichtmutable
, Referenztypen hinzuzufügen .So wie es ist, sind sie ohne Qualifikation unveränderlich.
Und da sie nicht sind ,
const
Qualifiziert, dann wäre es wohl mehr für verwirrendeis_const
Art auf einer Referenz treu zu ergeben.Ich halte dies für einen vernünftigen Kompromiss, zumal die Unveränderlichkeit ohnehin durch die bloße Tatsache erzwungen wird, dass keine Syntax zum Mutieren einer Referenz existiert.
quelle
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 dieextern
statische Speicherklasse und das Vorhandensein einer Verknüpfung angibt. Der Typ ist einfachconst 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 Typconst 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]
undconst 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 gelangenremove_reference
.Wenn Sie verwenden
ri
, wird die Referenz transparent aufgelöst, sodassri
"aussieht und sich anfühlti
" und als "Alias" dafür bezeichnet werden kann. Im Typsystem gibt esri
jedoch tatsächlich einen Typ, der " Verweis aufconst int
" ist.Warum sind Referenztypen?
Wenn Referenzen keine Typen wären, würden diese Funktionen denselben Typ haben:
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:
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
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 Deklarationconst int &ri = i
nicht einmal versucht , eineconst
qualifizierte Referenz zu erstellen: Es ist eine Referenz auf einen const-qualifizierten Typ (was selbst nicht der Fall istconst
). Genau wieconst in *ri
deklariert ein Zeiger auf etwasconst
, aber dieser Zeiger ist selbst nichtconst
.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
:Aber das macht nicht so, wie es aussieht. In dieser Erklärung ist es nicht ,
a
das bekommtconst
qualifiziert, aber die Elemente! Das heißt,a[0]
ist einconst int
, ist abera
nur "Array von int". Folglich ist hierfür keine Diagnose erforderlich:Das macht:
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 einemint *
Zeiger, als ob der Ausdruck verwendet&a[0]
worden wäre. Dies ist analog dazu, wie eine Referenzri
, 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 Referenzobjektsizeof 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.quelle
decltype
nicht der Fall ist diese transparente Auflösung durchführen (es ist keine Funktion, sori
wird nicht „gebraucht“ im Sinne Sie beschreiben). Dies passt sehr gut zu Ihrem gesamten Fokus auf das Typsystem - die Schlüsselverbindung ist, dassdecltype
es sich um eine Typsystemoperation handelt .const X & x ”bedeutet, dass x ein X-Objekt aliasisiert, aber Sie können dieses X-Objekt nicht über x ändern.
Und siehe std :: is_const .
quelle