Was bedeutet "int & foo ()" in C ++?

114

Beim Lesen dieser Erklärung zu lWerten und rWerten fielen mir diese Codezeilen auf:

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

Ich habe es in g ++ versucht, aber der Compiler sagt "undefinierter Verweis auf foo ()". Wenn ich hinzufüge

int foo()
{
  return 2;
}

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

Es wird gut kompiliert, aber das Ausführen führt zu einem Segmentierungsfehler . Nur die Linie

int& foo();

von selbst kompiliert und läuft ohne Probleme.

Was bedeutet dieser Code? Wie können Sie einem Funktionsaufruf einen Wert zuweisen und warum ist es kein r-Wert?

Sossisos
quelle
25
Sie weisen einem Funktionsaufruf keinen Wert zu, sondern der zurückgegebenen Referenz einen Wert .
Frédéric Hamidi
3
@ FrédéricHamidi weist dem Objekt, auf das sich die zurückgegebene Referenz bezieht, einen Wert zu
MM
3
Ja, es ist ein Alias ​​für das Objekt. Die Referenz ist nicht das Objekt
MM
2
@ RoyFalk-Deklarationen für lokale Funktionen sind Pocken. Ich persönlich würde mich freuen, wenn sie aus der Sprache entfernt würden. (Ohne Lambdas natürlich)
MM
4
Es gibt bereits einen GCC-Fehler dafür .
Jotik

Antworten:

168

Die Erklärung geht davon aus, dass es eine vernünftige Implementierung gibt, für foodie eine l-Wert-Referenz auf eine gültige zurückgegeben wird int.

Eine solche Implementierung könnte sein:

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

int& foo() {
    return a;
} 

Da nun fooeine lvalue-Referenz zurückgegeben wird, können wir dem Rückgabewert Folgendes zuweisen:

foo() = 42;

Dadurch wird der globale aWert mit dem Wert aktualisiert 42, den wir überprüfen können, indem wir direkt auf die Variable zugreifen oder fooerneut aufrufen :

int main() {
    foo() = 42;
    std::cout << a;     //prints 42
    std::cout << foo(); //also prints 42
}
TartanLlama
quelle
Vernünftig wie in Operator []? Oder ein anderes Mitglied Zugriffsmethoden?
Jan Dorniak
8
Angesichts der Verwirrung über Aktualisierungen ist es wahrscheinlich am besten, statische lokale Variablen aus den Beispielen zu entfernen. Die Initialisierung erhöht die unnötige Komplexität, was eine Hürde für das Lernen darstellt. Mach es einfach global.
Mooing Duck
72

Alle anderen Antworten deklarieren eine Statik innerhalb der Funktion. Ich denke, das könnte Sie verwirren. Schauen Sie sich das an:

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()eine Referenz zurückgegeben wird, können Sie ihr einen Wert zuweisen. Wenn dies ausgeführt wird, bwird es auf 11 geändert. Wenn Sie die Initialisierung so geändert haben a, dass sie beispielsweise 8 war, wird asie auf 11 geändert. Dies ist ein Code, der im Gegensatz zu den anderen Beispielen möglicherweise tatsächlich einen Zweck erfüllt.

Kate Gregory
quelle
5
Gibt es einen Grund, int a {3} anstelle von int a = 3 zu schreiben? Diese Syntax erscheint mir nicht angemessen.
Aleksei Petrenko
6
@AlexPetrenko das ist universelle Initialisierung. Ich habe es zufällig in dem Beispiel verwendet, das ich zur Hand hatte. Nichts Unangemessenes daran
Kate Gregory
3
Ich weiß was das ist. a = 3 fühlt sich einfach so viel sauberer an. Persönliche Präferenz)
Aleksei Petrenko
Genau wie das Deklarieren von Statik innerhalb einer Funktion einige Leute verwirren könnte, glaube ich auch, dass die Verwendung einer universellen Initialisierung genauso wahrscheinlich ist.
Ericcurtin
@AlekseiPetrenko Im Fall von int a = 1.3 wird implizit in a = 1 konvertiert, während int a {1.3} zu einem Fehler führt: Verengung der Konvertierung.
Sathvik
32
int& foo();

Deklariert eine Funktion namens foo, die einen Verweis auf a zurückgibt int. Was diese Beispiele nicht tun, ist, Ihnen eine Definition dieser Funktion zu geben, die Sie kompilieren könnten. Wenn wir verwenden

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

Jetzt haben wir eine Funktion, die einen Verweis auf zurückgibt bar. da bar ist static, wird es nach dem Aufruf der Funktion weiterleben, so dass die Rückgabe eines Verweises darauf sicher ist. Wenn wir das tun

foo() = 42;

Was passiert, ist, dass wir 42 zuweisen, barda wir der Referenz zuweisen und die Referenz nur ein Alias ​​für ist bar. Rufen wir die Funktion nochmal gerne auf

std::cout << foo();

Es würde 42 drucken, da wir bardas oben eingestellt haben.

NathanOliver
quelle
15

int &foo();deklariert eine Funktion, die foo()mit dem Rückgabetyp aufgerufen wird int&. Wenn Sie diese Funktion aufrufen, ohne einen Text anzugeben, wird wahrscheinlich ein undefinierter Referenzfehler angezeigt.

In Ihrem zweiten Versuch haben Sie eine Funktion bereitgestellt int foo(). Dies hat einen anderen Rückgabetyp als die von deklarierte Funktion int& foo();. Sie haben also zwei Deklarationen derselben foo, die nicht übereinstimmen, was gegen die One Definition Rule verstößt und undefiniertes Verhalten verursacht (keine Diagnose erforderlich).

Nehmen Sie für etwas, das funktioniert, 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 folgendermaßen aussehen:

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

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

int& foo();ist eine Funktion, die einen Verweis auf zurückgibt int. Ihre bereitgestellte Funktion wird intohne Referenz zurückgegeben.

Sie können tun

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

int main()
{
    int& foo();
    foo() = 42;
}
Jarod42
quelle
7

int & foo();bedeutet, dass foo()ein Verweis auf eine Variable zurückgegeben wird.

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.out k = 5

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

In Ihrem überarbeiteten Code wandeln Sie den zurückgegebenen Wert in eine Referenz um, die dann ungültig ist.

vy32
quelle
5

In diesem Zusammenhang bedeutet & eine Referenz - also gibt foo eine Referenz auf ein int und nicht auf ein int zurück.

Ich bin mir nicht sicher, ob Sie schon mit Zeigern gearbeitet hätten, aber es ist eine ähnliche Idee, dass Sie den Wert nicht tatsächlich aus der Funktion zurückgeben - stattdessen übergeben Sie die Informationen, die erforderlich sind, um den Speicherort im Speicher zu finden, an dem sich dieser befindet int ist.

Zusammenfassend lässt sich sagen, dass Sie einem Funktionsaufruf keinen Wert zuweisen. Sie verwenden eine Funktion, um eine Referenz abzurufen, und weisen dann den Wert, auf den verwiesen wird, einem neuen Wert zu. Es ist leicht zu glauben, dass alles auf einmal passiert, aber in Wirklichkeit erledigt der Computer alles in einer genauen Reihenfolge.

Wenn Sie sich fragen - der Grund, warum Sie einen Segfault erhalten, ist, dass Sie ein numerisches Literal '2' zurückgeben -, ist dies der genaue Fehler, den Sie erhalten würden, wenn Sie einen const int definieren und dann versuchen würden, ihn zu ändern Wert.

Wenn Sie noch nichts über Zeiger und dynamisches Gedächtnis gelernt haben, würde ich dies zunächst empfehlen, da es einige Konzepte gibt, die meiner Meinung nach schwer zu verstehen sind, es sei denn, Sie lernen sie alle gleichzeitig.

Dar Brett
quelle
4

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

In Ihrem Beispiel foowird eindeutig ein l-Wert basierend auf der Signatur zurückgegeben, aber Sie geben einen r-Wert zurück, der in einen l-Wert konvertiert wird. Dies ist eindeutig zum Scheitern verurteilt. Du könntest es tun:

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

und würde erfolgreich sein, indem der Wert von x geändert wird, wenn gesagt wird:

foo() = 10;
Eisfeuer
quelle
3

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

Nehmen wir also an, ursprünglich hat foo 5 zurückgegeben, und später, in Ihrer Hauptfunktion, sagen Sie foo() = 10;, druckt dann foo aus, es werden 10 statt 5 gedruckt.

Ich hoffe das ergibt Sinn :)

Ich bin auch neu in der Programmierung. Es ist interessant, solche Fragen zu sehen, die zum Nachdenken anregen! :) :)

psj01
quelle