C ++ übergibt ein Array als Referenz

78

Darf ein Array als Referenz übergeben werden?

 void foo(double& *bar) 

Scheint, dass mein Compiler nein sagt. Warum? Was ist der richtige Weg, um ein Array als Referenz zu übergeben? Oder eine Abhilfe? Ich habe ein Array-Argument, das meine Methode ändern und anschließend abrufen sollte. Alternativ könnte ich dieses Array zu einem Klassenmitglied machen, was gut funktioniert, aber viele Nachteile für einen anderen Teil meines Codes hat (den ich vermeiden möchte).

Danke und Grüße.

kiriloff
quelle
32
Überprüfen Sie die Spiralregel im Uhrzeigersinn .
Einige Programmierer Typ
1
Haben Sie darüber nachgedacht, ein std::vectoroder ähnliches zu verwenden?
Moooeeeep
1
@moooeeeep std::vectorist offensichtlich überlegen, wenn die Größe dynamisch / unbekannt ist, aber in Fällen, in denen die Größe immer gleich ist, wäre dies übertrieben und std::arraylöst stattdessen die syntaktische Hässlichkeit auf und verleiht gleichzeitig Wertesemantik.
underscore_d
warum nicht einfach foo (double bar []) aufheben? Wenn Sie möchten, können Sie auch die Länge des Arrays als void foo (doppelter Balken [2]) angeben. Sofern für einen komplexen Typ kein Kopierkonstruktor erforderlich ist.
ultimative Ursache

Antworten:

131

Arrays können eigentlich nur als Referenz übergeben werden:

void foo(double (&bar)[10])
{
}

Dies hindert Sie daran, Dinge zu tun wie:

double arr[20];
foo(arr); // won't compile

Um ein Array beliebiger Größe übergeben zu können foo, machen Sie es zu einer Vorlage und erfassen Sie die Größe des Arrays zur Kompilierungszeit:

template<typename T, size_t N>
void foo(T (&bar)[N])
{
    // use N here
}

Sie sollten ernsthaft in Betracht ziehen, zu verwenden std::vector , oder wenn Sie einen Compiler haben, der c ++ 11 unterstützt std::array.

jrok
quelle
7
"Sobald foodas Array an übergeben wurde, zerfällt es in einen Zeiger." Nein, und da Sie nur ein Array von 10 Elementen übergeben können, benötigen Sie keine weiteren Informationen zur Größe. (Und noch einmal, Sie können kein Array an übergeben void foo( double*& ); die Ergebnisse einer impliziten Konvertierung sind ein r-Wert und können nicht verwendet werden, um einen Verweis auf Nicht-Konstante zu initialisieren. Sie müssen verwenden void foo( double *const & );.
James Kanze
1
super :) genau das suche ich. Ich habe es void foo(T &bar[N])in meiner Vorlage gemacht - aber das bedeutet eigentlich "ein Array von Referenzen" und nicht "Verweis auf Array".
OLL
Wenn eine solche Funktion bereits vorhanden ist und Sie nur eine std::vector(von der richtigen Größe) haben, wie würden Sie den Inhalt des Vektors (möglicherweise erhalten über std::vector::data()) in diesen Array-Typ umwandeln? Wie lautet die Syntax für eine feste Arraygröße in einem Typparameter (dh zur Verwendung mit static_cast<>)?
DavidA
1
@meowsqueak Das würdest du nicht, weil die Besetzung ein undefiniertes Verhalten hätte, AFAICT; Ich kann nicht berücksichtigen, reinterpret_castdass ein dynamisch zugewiesenes Array (ohne intrinsische Größe nach der Zuweisung) als automatisches Array mit fester Größe behandelt wird. Außerdem ist das die falsche Frage: Die richtige Frage wäre "Wie kann ich meinen Code umgestalten, um mit beiden Containertypen zu arbeiten?", Und die Antwort lautet Vorlagen und Iteratoren / Bereiche oder, da Sie sagten, die Größe ist fest, eine Funktion das braucht nur einen Datenzeiger / Ref und codiert die Größe fest (oder wenn die Größe variieren könnte, einen Daten- / span
underscore_d
warum nicht einfach foo (double bar []) aufheben? Wenn Sie möchten, können Sie auch die Länge des Arrays als void foo (doppelter Balken [2]) angeben. Sofern für einen komplexen Typ kein Kopierkonstruktor erforderlich ist.
ultimative Ursache
18

Ja, aber wenn Argumente für eine Referenz übereinstimmen, ist das implizite Array zum Zeiger nicht automatisch. Sie benötigen also Folgendes:

void foo( double (&array)[42] );

oder

void foo( double (&array)[] );

Beachten Sie jedoch, dass bei der Zuordnung, double [42]und double []sind verschiedene Typen. Wenn Sie ein Array mit einer unbekannten Dimension haben, stimmt es mit dem zweiten, aber nicht mit dem ersten überein, und wenn Sie ein Array mit 42 Elementen haben, stimmt es mit dem ersten, aber nicht mit dem zweiten überein . (Letzteres ist meiner Meinung nach sehr kontraintuitiv.)

Im zweiten Fall müssen Sie die Dimension ebenfalls übergeben, da es keine Möglichkeit gibt, sie wiederherzustellen, sobald Sie sich in der Funktion befinden.

James Kanze
quelle
7
Der zweite kompiliert nicht für mich.
Jrok
Für mich auch nicht. Ich weiß nicht, ob es an einem Compiler-Fehler oder etwas in dem Standard liegt, den ich übersehen habe. Ich wollte es eigentlich nie machen. (Die erste ist sehr nützlich, wenn die Dimension ein Vorlagenparameter ist, da der Compiler die richtige Dimension für Sie herausfindet.)
James Kanze
6
Ich glaube nicht, dass das zweite Beispiel ein gültiger Standard ist, der C ++ entspricht. Im Übrigen wird es nicht mit GCC 4.8 oder Clang 3.4 kompiliert. Ich denke, eine Vorlagengröße ist erforderlich.
Ricky65
2
@ Ricky65 So ist es. Ich wundere mich warum. Verweise auf unvollständige Typen sind normalerweise als Funktionsparameter zulässig. (Natürlich ändert sich in diesem Fall der Typ, wenn er abgeschlossen ist: double []und double [42]es handelt sich um zwei verschiedene Typen. Das einzige, was Sie an die letztere Form übergeben könnten, wäre ein Array unbekannter Länge, z. B. extern double d[];oder ein solches schränkt die Benutzerfreundlichkeit ein.)
James Kanze
6

Wenn Sie nur die Elemente ändern möchten:

void foo(double *bar);

reicht.

Wenn Sie die Adresse in (zB :) ändern möchten, reallocdies jedoch für Arrays nicht funktioniert:

void foo(double *&bar);

ist die richtige Form.

Naszta
quelle
4
Außer dass Sie kein Array an letzteres übergeben können. Er fragte nach Arrays, nicht nach Zeigern.
James Kanze
@ JamesKanze: stimmt. Der zweite Fall funktioniert nicht mit Arrays auf dem Stapel. In diesem Fall sollte er das erste Formular verwenden.
Naszta
8
Der zweite Fall funktioniert nicht für Arrays, Punkt. Dies funktioniert nur, wenn Sie eine tatsächliche Variable vom Zeigertyp haben.
James Kanze
6

Wenn Sie C ++ verwenden, ist der obligatorische Vorschlag, der hier noch fehlt, zu verwenden std::vector<double> .

Sie können es leicht als Referenz übergeben:

void foo(std::vector<double>& bar) {}

Und wenn Sie C ++ 11-Unterstützung haben, schauen Sie sich auch an std::array .

Als Referenz:

moooeeeep
quelle
5
:-) Ja. std::vectorist normalerweise die beste Lösung. Es gibt jedoch Ausnahmen.
James Kanze
3

8.3.5.8 Wenn der Typ eines Parameters einen Typ der Form "Zeiger auf Array mit unbekannter Grenze von T" oder "Verweis auf Array mit unbekannter Grenze von T" enthält, ist das Programm fehlerhaft

Kirilodius
quelle
2

Wie die andere Antwort sagt, setzen Sie das &nach dem *.

Dies bringt einen interessanten Punkt hervor, der manchmal verwirrend sein kann: Typen sollten von rechts nach links gelesen werden. Dies ist beispielsweise (beginnend ganz rechts *) ein Zeiger auf einen konstanten Zeiger auf ein int.

int * const *x;

Was Sie geschrieben haben, wäre daher ein Zeiger auf eine Referenz, was nicht möglich ist.

Parkovski
quelle
Durch Ändern der Reihenfolge kann er immer noch kein Array übergeben. Es erfordert ein tatsächliches Zeigerobjekt, nicht die Ergebnisse einer Konvertierung.
James Kanze
1

Hier erklärt Erik jede Art und Weise , wie ein Array durch Referenz übergeben wird : https://stackoverflow.com/a/5724184/5090928 .

Ebenso können Sie eine Array-Referenzvariable wie folgt erstellen :

int arr1[] = {1, 2, 3, 4, 5};
int(&arr2)[5] = arr1;
serg06
quelle