webentwicklung-frage-antwort-db.com.de

Warum sind Arrays von Referenzen illegal?

Der folgende Code wird nicht kompiliert.

int a = 1, b = 2, c = 3;
int& arr[] = {a,b,c,8};

Was sagt der C++ - Standard dazu?

Ich weiß, ich könnte eine Klasse deklarieren, die eine Referenz enthält, und dann ein Array dieser Klasse erstellen, wie unten gezeigt. Aber ich möchte wirklich wissen, warum der obige Code nicht kompiliert wird.

struct cintref
{
    cintref(const int & ref) : ref(ref) {}
    operator const int &() { return ref; }
private:
    const int & ref;
    void operator=(const cintref &);
};

int main() 
{
  int a=1,b=2,c=3;
  //typedef const int &  cintref;
  cintref arr[] = {a,b,c,8};
}

Es ist möglich, struct cintref anstelle von const int & zu verwenden, um ein Array von Referenzen zu simulieren.

121
Alexey Malistov

Zur Beantwortung Ihrer Frage nach dem Standard kann ich den C++ - Standard §8.3.2/4 zitieren: /:

Es darf keine Referenzen auf Referenzen, keine Arrays von Referenzen und keine Hinweise auf Referenzen geben. 

132

Referenzen sind keine Objekte. Sie haben keinen eigenen Speicher, sie referenzieren nur vorhandene Objekte. Aus diesem Grund ist es nicht sinnvoll, über Referenzen zu verfügen.

Wenn Sie ein leichtes object wünschen, das auf ein anderes Objekt verweist, können Sie einen Zeiger verwenden. Sie können eine struct mit einem Referenzelement nur als Objekte in Arrays verwenden, wenn Sie die explizite Initialisierung für alle Referenzelemente für alle struct-Instanzen bereitstellen. Referenzen können nicht standardmäßig initialisiert werden.

Editieren: Wie in den Anmerkungen bemerkt, besteht im Standardabschnitt zu Erklärungen ein explizites Verbot von Verweisfeldern.

53
CB Bailey

Dies ist eine interessante Diskussion. Offensichtlich sind Arrays von Arrays absolut illegal, aber der Grund dafür ist meiner Meinung nach nicht so einfach zu sagen: "Sie sind keine Objekte" oder "sie haben keine Größe". Ich möchte darauf hinweisen, dass Arrays selbst keine vollwertigen Objekte in C/C++ sind. Wenn Sie dem widersprechen, versuchen Sie, einige stl-Schablonenklassen mit einem Array als 'Klassen'-Schablonenparameter zu instantiieren und zu sehen, was passiert. Sie können sie nicht zurückgeben, zuweisen, als Parameter übergeben. (Ein Array-Parameter wird als Zeiger behandelt). Arrays von Arrays sind jedoch zulässig. Referenzen haben eine Größe, die der Compiler berechnen kann und muss - Sie können eine Referenz nicht sizeof (), aber Sie können eine Struktur erstellen, die nur Referenzen enthält. Es hat eine Größe, die ausreicht, um alle Zeiger zu enthalten, die die Referenzen implementieren. Sie können eine solche Struktur nicht instanziieren, ohne alle Elemente zu initialisieren:

struct mys {
 int & a;
 int & b;
 int & c;
};
...
int ivar1, ivar2, arr[200];
mys my_refs = { ivar1, ivar2, arr[12] };

my_refs.a += 3  ;  // add 3 to ivar1

Tatsächlich können Sie diese Zeile zur Strukturdefinition hinzufügen

struct mys {
 ...
 int & operator[]( int i ) { return i==0?a : i==1? b : c; }
};

... und jetzt habe ich etwas, das VIEL wie ein Array von Refs aussieht:

int ivar1, ivar2, arr[200];
mys my_refs = { ivar1, ivar2, arr[12] };

my_refs[1] = my_refs[2]  ;  // copy arr[12] to ivar2
&my_refs[0];               // gives &my_refs.a == &ivar1

Nun, dies ist kein echtes Array, sondern eine Operatorüberladung. Dinge, die Arrays normalerweise nicht tun, wie zum Beispiel sizeof (arr)/sizeof (arr [0]). Aber es macht genau das, was ich mit einer Reihe von Referenzen erreichen möchte, mit absolut legalem C++ Außer (a) ist es mühsam, für mehr als 3 oder 4 Elemente einzurichten, und (b) führt es eine Berechnung mit a durch Bündel von?: was mit Hilfe der Indizierung (nicht mit der normalen C-Pointer-Berechnungs-Semantik-Indizierung, aber trotzdem der Indizierung) durchgeführt werden kann. Ich würde gerne einen sehr begrenzten "Array von Referenztypen" sehen, der dies tatsächlich kann. D.h. Ein Array von Referenzen würde nicht als ein allgemeines Array von Dingen behandelt werden, die Referenzen sind, sondern vielmehr ein neues "Array-of-Reference" -Ding, das effektiv einer intern generierten Klasse ähnlich der oben genannten (aber dem von Ihnen) entspricht leider kann nicht mit Vorlagen machen).

dies würde wahrscheinlich funktionieren, wenn Sie sich nicht für diese Art von Boshaftigkeit interessieren: Stellen Sie '* this' als Array von Int * 's neu ein und geben Sie eine Referenz aus einem der folgenden Werte zurück: (nicht empfohlen, zeigt jedoch, wie das richtige' Array ' würde funktionieren):

 int & operator[]( int i ) { return *(reinterpret_cast<int**>(this)[i]); }
27
greggo

Kommentar zu deiner Bearbeitung:

Bessere Lösung ist std::reference_wrapper.

Details: http://www.cplusplus.com/reference/functional/reference_wrapper/

Beispiel:

#include <iostream>
#include <functional>
using namespace std;

int main() {
    int a=1,b=2,c=3,d=4;
    using intlink = std::reference_wrapper<int>;
    intlink arr[] = {a,b,c,d};
    return 0;
}
24
Youw

Ein Array ist implizit in einen Zeiger konvertierbar, und Zeiger-zu-Verweis ist in C++ unzulässig

11
EFraim

Denn wie viele hier gesagt haben, sind Referenzen keine Objekte. Sie sind einfach Aliase. Es ist richtig, dass einige Compiler sie als Zeiger implementieren, aber der Standard erzwingt dies nicht. Und da Verweise keine Objekte sind, können Sie nicht auf sie verweisen. Das Speichern von Elementen in einem Array bedeutet, dass es eine Art Indexadresse gibt (d. H. Auf Elemente mit einem bestimmten Index zeigt). Deshalb können Sie keine Arrays von Referenzen haben, weil Sie nicht auf sie zeigen können.

Verwenden Sie stattdessen boost :: reference_wrapper oder boost :: Tuple. oder nur Zeiger.

9
navigator

Was ist mit int& arr[] = {a,b,c,8};sizeof(*arr)?

Überall wird eine Referenz so behandelt, als wäre sie nur das Ding selbst, daher sollte sizeof(*arr) einfach sizeof(int) sein. Dies würde jedoch die Array-Zeigerarithmetik für dieses Array falsch machen (vorausgesetzt, dass Referenzen nicht die gleichen Breiten haben, ist ints). Um die Mehrdeutigkeit zu beseitigen, ist es verboten. 

9
PaulMurrayCbr

Mit dieser Vorlagenstruktur können Sie sich ziemlich nahe kommen. Sie müssen jedoch mit Ausdrücken initialisieren, bei denen es sich um Zeiger auf T und nicht um T handelt. Obwohl Sie auf einfache Weise einen 'fake_constref_array' erstellen können, können Sie dies nicht an r-Werte binden, wie im OP-Beispiel ('8') beschrieben.

#include <stdio.h>

template<class T, int N> 
struct fake_ref_array {
   T * ptrs[N];
  T & operator [] ( int i ){ return *ptrs[i]; }
};

int A,B,X[3];

void func( int j, int k)
{
  fake_ref_array<int,3> refarr = { &A, &B, &X[1] };
  refarr[j] = k;  // :-) 
   // You could probably make the following work using an overload of + that returns
   // a proxy that overloads *. Still not a real array though, so it would just be
   // stunt programming at that point.
   // *(refarr + j) = k  
}

int
main()
{
    func(1,7);  //B = 7
    func(2,8);     // X[1] = 8
    printf("A=%d B=%d X = {%d,%d,%d}\n", A,B,X[0],X[1],X[2]);
        return 0;
}

-> A = 0 B = 7 X = {0,8,0}

4
greggo

Wenn Sie etwas in einem Array speichern, muss dessen Größe bekannt sein (da die Indexierung des Arrays von der Größe abhängt). Gemäß dem C++ - Standard Es ist nicht angegeben, ob für eine Referenz Speicherplatz erforderlich ist oder nicht. Als Ergebnis wäre die Indexierung eines Arrays von Referenzen nicht möglich.

2
Pradyot

Ein Referenzobjekt hat keine Größe. Wenn Sie sizeof(referenceVariable) schreiben, erhalten Sie die Größe des Objekts, auf das mit referenceVariable verwiesen wird, nicht die der Referenz selbst. Es hat keine eigene Größe, weshalb der Compiler nicht berechnen kann, wie viel das Array benötigen würde.

2
Carl Seleborg

Fügen Sie einfach alle Konversationen hinzu. Da Arrays aufeinanderfolgende Speicherorte zum Speichern des Elements benötigen, können wir bei der Erstellung eines Arrays von Referenzen nicht garantieren, dass sie sich an aufeinanderfolgenden Speicherorten befinden Wir können nicht alle mathematischen Operationen auf Array anwenden.

1
Amit Kumar

Betrachten Sie ein Array von Zeigern. Ein Zeiger ist wirklich eine Adresse; Wenn Sie also das Array initialisieren, sagen Sie dem Computer analog, dass "dieser Speicherblock für diese X-Nummern (die Adressen anderer Elemente sind)" zugewiesen werden. Wenn Sie dann einen der Zeiger ändern, ändern Sie einfach, worauf er zeigt. Es ist immer noch eine numerische Adresse, die selbst an derselben Stelle sitzt.

Eine Referenz ist analog zu einem Alias. Wenn Sie ein Array von Referenzen deklarieren würden, würden Sie dem Computer im Grunde sagen, "ordnen Sie diesen amorphen Speicherblock aus all diesen verschiedenen Elementen zu."

0
Dan Tao

Ich glaube, die Antwort ist sehr einfach und hat mit semantischen Referenzregeln und dem Umgang mit Arrays in C++ zu tun.

Kurz gesagt: Verweise können als Strukturen betrachtet werden, die keinen Standardkonstruktor haben. Daher gelten alle gleichen Regeln.

1) Semantisch haben Referenzen keinen Standardwert. Referenzen können nur durch einen Verweis erstellt werden. Referenzen haben keinen Wert, um das Fehlen einer Referenz darzustellen.

2) Bei der Zuweisung eines Arrays der Größe X erstellt das Programm eine Sammlung von Objekten mit Standardinitialisierung. Da reference keinen Standardwert hat, ist das Erstellen eines solchen Arrays semantisch illegal.

Diese Regel gilt auch für Strukturen/Klassen, die keinen Standardkonstruktor haben. Das folgende Codebeispiel kann nicht kompiliert werden:

struct Object
{
    Object(int value) { }
};

Object objects[1]; // Error: no appropriate default constructor available
0
Kristupas A.