2016-04-07 19 views
104

Während this explanation auf lvalues ​​und rvalues ​​lesen, stecken diese Zeilen Code zu mir:Was bedeutet "int & foo()" in C++?

int& foo(); 
foo() = 42; // OK, foo() is an lvalue 

ich es in g ++ versucht, aber der Compiler sagt: "undefined Verweis auf foo()". Wenn ich

int foo() 
{ 
    return 2; 
} 

int main() 
{ 
    int& foo(); 
    foo() = 42; 
} 

hinzufügen kompiliert es in Ordnung, aber es läuft gibt einen segmentation fault. Nur die Linie

int& foo(); 

von sich aus kompiliert und läuft ohne Probleme.

Was bedeutet dieser Code? Wie können Sie einem Funktionsaufruf einen Wert zuweisen, und warum ist das kein Rvalue?

+22

Sie weisen * einem Funktionsaufruf * keinen Wert zu, da Sie * der Referenz, die zurückgegeben wird * einen Wert zuweisen. –

+2

@ FrédéricHamidi weist dem Objekt einen Wert zu, auf den sich die zurückgegebene Referenz bezieht –

+0

@ M.M, die Referenz * ist * das Objekt - es ist ein Alias. –

Antwort

160

Die Erklärung wird unter der Annahme, dass es für foo, die gibt einen L-Wert-Verweis auf ein gültiges int eine vernünftige Implementierung ist.

Eine solche Implementierung könnte sein:

int a = 2; //global variable, lives until program termination 

int& foo() { 
    return a; 
} 

Jetzt, da foo ein L-Wert Referenz zurückgibt, können wir etwas auf den Rückgabewert, wie so zuweisen:

foo() = 42; 

Dies aktualisiert die globale a mit dem Wert 42, den wir überprüfen können, indem wir direkt auf die Variable zugreifen oder foo erneut aufrufen:

int main() { 
    foo() = 42; 
    std::cout << a;  //prints 42 
    std::cout << foo(); //also prints 42 
} 
+0

Angemessen wie im Operator []? Oder einige andere Mitglieder Zugriffsmethoden? –

+8

Angesichts der Verwirrung über Refrences ist es wahrscheinlich am besten, statische lokale Variablen aus den Stichproben zu entfernen. Die Initialisierung fügt unnötige Komplexität hinzu, was eine Lernhürde darstellt. Mach es einfach global. –

+1

@MooingDuck Messe, ich habe die Antwort aktualisiert. – TartanLlama

9

int& foo(); ist eine Funktion, die einen Verweis auf int zurückgibt. Ihre angegebene Funktion gibt int ohne Referenz zurück.

können Sie tun

int& foo() 
{ 
    static int i = 42; 
    return i; 
} 

int main() 
{ 
    int& foo(); 
    foo() = 42; 
} 
3

Der Beispielcode auf der verknüpften Seite ist nur eine Dummy-Funktionsdeklaration. Es kompiliert nicht, aber wenn Sie eine Funktion definiert haben, würde es allgemein funktionieren. Das Beispiel meinte "Wenn Sie eine Funktion mit dieser Signatur hätten, könnten Sie sie so verwenden".

In Ihrem Beispiel gibt foo eindeutig einen Lvalue basierend auf der Signatur zurück, aber Sie geben einen Rvalue zurück, der in einen Lvalue konvertiert wird. Dies ist eindeutig zum Scheitern verurteilt. Sie tun können:

int& foo() 
{ 
    static int x; 
    return x; 
} 

und erfolgreich sein würde, indem der Wert von x ändert, wenn ich sage:

foo() = 10; 
6

int & foo(); bedeutet, dass foo() auf eine Variable einen Verweis zurückgibt.

Betrachten Sie diesen Code:

#include <iostream> 
int k = 0; 

int &foo() 
{ 
    return k; 
} 

int main(int argc,char **argv) 
{ 
    k = 4; 
    foo() = 5; 
    std::cout << "k=" << k << "\n"; 
    return 0; 
} 

Dieser Code druckt:

./a $.aus k = 5

Weil foo() einen Verweis auf die globale Variable k zurückgibt.

In Ihrem überarbeiteten Code wird der zurückgegebene Wert in eine Referenz umgewandelt, die dann ungültig ist.

30
int& foo(); 

Deklariert eine Funktion mit dem Namen foo, die einen Verweis auf ein int zurückgibt. Was diese Beispiele nicht tun, ist eine Definition der Funktion, die Sie kompilieren könnten. Wenn wir

int & foo() 
{ 
    static int bar = 0; 
    return bar; 
} 

verwenden Jetzt haben wir eine Funktion, die einen Verweis auf bar zurückgibt. da der Balken static ist, wird er nach dem Aufruf der Funktion weiterleben, so dass ein Verweis darauf sicher ist. Nun, wenn wir

tun
foo() = 42; 

Was passiert ist, wir 42-bar zuweisen, da wir auf die Referenz und die Referenz ist nur ein Alias ​​für bar zuweisen. Wenn wir die Funktion wieder wie

std::cout << foo(); 

nennen würde es 42 drucken, da wir bar die oben gesetzt.

12

int &foo(); deklariert eine Funktion namens foo() mit Rückgabetyp int&. Wenn Sie diese Funktion aufrufen, ohne einen Körper anzugeben, erhalten Sie wahrscheinlich einen nicht definierten Referenzfehler.

In Ihrem zweiten Versuch haben Sie eine Funktion int foo() zur Verfügung gestellt. Dies hat einen anderen Rückgabetyp als die Funktion, die von int& foo(); deklariert wird. Sie haben also zwei Deklarationen derselben foo, die nicht übereinstimmen, was gegen die eine Definitionsregel verstößt, die zu undefiniertem Verhalten führt (keine Diagnose erforderlich).

Für etwas, das funktioniert, nehmen Sie die lokale Funktionsdeklaration heraus. Sie können zu stillem undefiniertem Verhalten führen, wie Sie gesehen haben. Verwenden Sie stattdessen nur Funktionsdeklarationen außerhalb einer Funktion. Ihr Programm könnte wie folgt aussehen:

int &foo() 
{ 
    static int i = 2; 
    return i; 
} 

int main() 
{ 
    ++foo(); 
    std::cout << foo() << '\n'; 
} 
5

In diesem Zusammenhang ist die & bedeutet eine Referenz - so foo einen Verweis auf ein int zurückgibt, anstatt ein int.

Ich bin mir nicht sicher, ob Sie mit Zeigern bereits gearbeitet hätten, aber es ist eine ähnliche Idee, Sie geben den Wert nicht wirklich aus der Funktion zurück - stattdessen übergeben Sie die Informationen, um den Standort zu finden im Speicher, wo das Int ist.

Um zusammenzufassen, weisen Sie einem Funktionsaufruf keinen Wert zu - Sie verwenden eine Funktion, um eine Referenz zu erhalten, und weisen dann den Wert, auf den verwiesen wird, einem neuen Wert zu. Es ist leicht zu denken, dass alles auf einmal passiert, aber in Wirklichkeit macht der Computer alles in einer genauen Reihenfolge.

Wenn Sie sich wundern - der Grund, warum Sie einen segfault erhalten, ist, weil Sie ein numerisches Literal '2' zurückgeben - also ist es genau der Fehler, den Sie bekommen würden, wenn Sie ein const int und dann definieren würden versuche, seinen Wert zu ändern.

Wenn Sie noch nicht über Zeiger und dynamisches Gedächtnis gelernt haben, dann würde ich das zuerst empfehlen, da es ein paar Konzepte gibt, von denen ich denke, dass sie schwer zu verstehen sind, es sei denn, Sie lernen sie alle gleichzeitig.

3

Die Funktion, die Sie haben, foo(), ist eine Funktion, die einen Verweis auf eine ganze Zahl zurückgibt.

also lasst uns sagen ursprünglich foo 5 zurück, und später, in der Hauptfunktion, sagen Sie foo() = 10;, dann foo druckt, wird es 5. 10 statt

drucken hoffe ich, dass Sinn macht :)

Ich bin neu in der Programmierung als auch. Es ist interessant, solche Fragen zu sehen, die einen nachdenken lassen! :)

65

Alle anderen Antworten erklären eine statische innerhalb der Funktion. Ich denke, dass verwirren könnten Sie nehmen so einen Blick auf diese:

int& highest(int & i, int & j) 
{ 
    if (i > j) 
    { 
     return i; 
    } 
    return j; 
} 

int main() 
{ 
    int a{ 3}; 
    int b{ 4 }; 
    highest(a, b) = 11; 
    return 0; 
} 

Da highest() gibt eine Referenz, können Sie einen Wert zuweisen können. Wenn dies ausgeführt wird, wird b in 11 geändert. Wenn Sie die Initialisierung so geändert haben, dass a beispielsweise 8 war, dann würde a auf 11 geändert. Dies ist ein Code, der tatsächlich einen Zweck erfüllen könnte, im Gegensatz zu den anderen Beispielen.

+4

Gibt es einen Grund, int a {3} anstelle von int a = 3 zu schreiben? Diese Syntax scheint mir nicht angemessen. –

+5

@AlexPetrenko das ist universelle Initialisierung. Ich benutzte es zufällig in dem Beispiel, das ich zur Hand hatte. Nichts ungeeignet darüber –

+3

Ich weiß was das ist. a = 3 fühlt sich einfach viel sauberer an. Persönliche Präferenz) –