Hinweis: Diese Antwort gilt nur für C ++ 11 und höher. Es gibt kein "C / C ++", es sind verschiedene Sprachen.
Nein, es besteht keine Gefahr, ein lokales Objekt nach Wert zurückzugeben, und es wird empfohlen, dies zu tun. Ich denke jedoch, dass in allen Antworten hier ein wichtiger Punkt fehlt. Viele andere haben gesagt, dass die Struktur entweder kopiert oder direkt mit RVO platziert wird. Dies ist jedoch nicht ganz richtig. Ich werde versuchen, genau zu erklären, welche Dinge bei der Rückgabe eines lokalen Objekts passieren können.
Semantik verschieben
Seit c ++ 11 haben wir rWertreferenzen, die auf temporäre Objekte verweisen, denen sicher gestohlen werden kann. Beispielsweise verfügt std :: vector über einen Verschiebungskonstruktor sowie einen Verschiebungszuweisungsoperator. Beide haben eine konstante Komplexität und kopieren einfach den Zeiger auf die Daten des Vektors, von dem verschoben wird. Ich werde hier nicht näher auf die Bewegungssemantik eingehen.
Da ein lokal innerhalb einer Funktion erstelltes Objekt temporär ist und bei der Rückkehr der Funktion den Gültigkeitsbereich verlässt, wird ein zurückgegebenes Objekt ab C ++ 11 niemals mehr kopiert. Der Verschiebungskonstruktor wird für das zurückgegebene Objekt aufgerufen (oder nicht, wie später erläutert). Dies bedeutet, dass, wenn Sie ein Objekt mit einem teuren Kopierkonstruktor, aber einem kostengünstigen Verschiebungskonstruktor wie einem großen Vektor zurückgeben, nur das Eigentum an den Daten vom lokalen Objekt auf das zurückgegebene Objekt übertragen wird - was billig ist.
Beachten Sie, dass es in Ihrem speziellen Beispiel keinen Unterschied zwischen dem Kopieren und Verschieben des Objekts gibt. Die Standardkonstruktoren zum Verschieben und Kopieren Ihrer Struktur führen zu denselben Operationen. Kopieren von zwei ganzen Zahlen. Dies ist jedoch mindestens so schnell wie jede andere Lösung, da die gesamte Struktur in ein 64-Bit-CPU-Register passt (korrigieren Sie mich, wenn ich falsch liege, ich kenne nicht viele CPU-Register).
RVO und NRVO
RVO bedeutet Rückgabewertoptimierung und ist eine der wenigen Optimierungen, die Compiler durchführen und die Nebenwirkungen haben können. Seit c ++ 17 ist RVO erforderlich. Wenn ein unbenanntes Objekt zurückgegeben wird, wird es direkt an der Stelle erstellt, an der der Aufrufer den zurückgegebenen Wert zuweist. Weder der Kopierkonstruktor noch der Verschiebungskonstruktor werden aufgerufen. Ohne RVO würde das unbenannte Objekt zuerst lokal erstellt, dann in der zurückgegebenen Adresse erstellt und dann das lokale unbenannte Objekt zerstört.
Beispiel, bei dem RVO erforderlich ist (c ++ 17) oder wahrscheinlich (vor c ++ 17):
auto function(int a, int b) -> MyStruct {
// ...
return MyStruct{a, b};
}
NRVO bedeutet Named Return Value Optimization und ist dasselbe wie RVO, außer dass es für ein benanntes Objekt durchgeführt wird, das lokal für die aufgerufene Funktion ist. Dies wird vom Standard (c ++ 20) immer noch nicht garantiert, aber viele Compiler tun dies immer noch. Beachten Sie, dass selbst bei benannten lokalen Objekten diese bei der Rückgabe im schlimmsten Fall verschoben werden.
Fazit
Der einzige Fall, in dem Sie in Betracht ziehen sollten, nicht nach Wert zurückzukehren, ist, wenn Sie ein benanntes, sehr großes Objekt (wie in seiner Stapelgröße) haben. Dies liegt daran, dass NRVO (ab c ++ 20) noch nicht garantiert ist und selbst das Bewegen des Objekts langsam wäre. Meine Empfehlung und die Empfehlung in den Cpp-Kernrichtlinien lautet, Objekte immer nach Wert zurückzugeben (wenn mehrere Rückgabewerte verwendet werden, verwenden Sie struct (oder tuple)), wobei die einzige Ausnahme darin besteht, dass das Verschieben des Objekts teuer ist. Verwenden Sie in diesem Fall einen nicht konstanten Referenzparameter.
Es ist NIE eine gute Idee, eine Ressource zurückzugeben, die manuell von einer Funktion in C ++ freigegeben werden muss. TU das niemals. Verwenden Sie mindestens ein std :: unique_ptr oder erstellen Sie eine eigene nicht lokale oder lokale Struktur mit einem Destruktor, der seine Ressource ( RAII ) freigibt, und geben Sie eine Instanz davon zurück. Es wäre dann auch eine gute Idee, den Verschiebungskonstruktor und den Verschiebungszuweisungsoperator zu definieren, wenn die Ressource keine eigene Verschiebungssemantik hat (und den Kopierkonstruktor / die Zuweisung löschen).