Was bedeutet die Anweisung "return {}" in C ++ 11?

115

Was macht die Aussage

return {};

in C ++ 11 angeben, und wann es anstelle von (sagen) zu verwenden ist

return NULL;

oder

return nullptr;
Pedia
quelle
59
Es gibt eine standardmäßig erstellte Instanz des Rückgabetyps der Funktion zurück.
Richard Hodges
Oder ist es einfach return;ohne Wert?
i486
Nein, wie die Diskussion zeigt, ist es ein Fehler bei der Kompilierung, wenn Ihre Funktion etwas zurückgeben sollte (dh nicht vom Typ void void) und Sie nur schreiben return; . Andererseits return{};ist es gültig, wenn Sie einen Rückgabetyp haben.
Pedia
@Pedia Nicht immer, einige Objekte benötigen Argumente, um zu konstruieren
MM

Antworten:

108

return {};gibt "ein Objekt des Rückgabetyps der Funktion zurückgeben, das mit einem leeren Listeninitialisierer initialisiert wurde " an. Das genaue Verhalten hängt vom Typ des zurückgegebenen Objekts ab.

Von cppreference.com (da das OP mit C ++ 11 gekennzeichnet ist, habe ich die Regeln in C ++ 14 und C ++ 17 ausgeschlossen; weitere Details finden Sie unter dem Link):

  • Wenn die Klammer-Init-Liste leer ist und T ein Klassentyp mit einem Standardkonstruktor ist, wird eine Wertinitialisierung durchgeführt.
  • Andernfalls wird, wenn T ein Aggregattyp ist, eine Aggregatinitialisierung durchgeführt.
  • Wenn T eine Spezialisierung von std :: initializer_list ist, wird das T-Objekt je nach Kontext direkt oder kopierinitialisiert aus der geschweiften Init-Liste.
  • Ansonsten werden die Konstruktoren von T in zwei Phasen betrachtet:

    • Alle Konstruktoren, die std :: initializer_list als einziges Argument oder als erstes Argument verwenden, wenn die verbleibenden Argumente Standardwerte haben, werden untersucht und durch Überladungsauflösung mit einem einzelnen Argument vom Typ std :: initializer_list abgeglichen
    • Wenn die vorherige Stufe keine Übereinstimmung erzeugt, nehmen alle Konstruktoren von T an der Überladungsauflösung für die Menge von Argumenten teil, die aus den Elementen der geschweiften Init-Liste besteht, mit der Einschränkung, dass nur nicht einschränkende Konvertierungen zulässig sind. Wenn in dieser Phase ein expliziter Konstruktor als beste Übereinstimmung für eine Kopierlisteninitialisierung erstellt wird, schlägt die Kompilierung fehl (beachten Sie, dass bei der einfachen Kopierinitialisierung explizite Konstruktoren überhaupt nicht berücksichtigt werden).
  • Andernfalls (wenn T kein Klassentyp ist), wenn die Klammer-Init-Liste nur ein Element enthält und entweder T kein Referenztyp oder ein Referenztyp ist, der mit dem Typ des Elements kompatibel ist, ist T direkt- initialisiert (bei direkter Listeninitialisierung) oder kopierinitialisiert (bei Kopierlisteninitialisierung), außer dass eine Einschränkung der Konvertierungen nicht zulässig ist.

  • Andernfalls, wenn T ein Referenztyp ist, der nicht mit dem Typ des Elements kompatibel ist. (Dies schlägt fehl, wenn die Referenz eine nicht konstante Wertreferenz ist.)
  • Andernfalls wird T wertinitialisiert, wenn die Klammer-Init-Liste keine Elemente enthält.

Vor C ++ 11 hätten Sie für eine Funktion, die a zurückgibt std::string, Folgendes geschrieben:

std::string get_string() {
    return std::string();
}

Wenn Sie die Klammer-Syntax in C ++ 11 verwenden, müssen Sie den Typ nicht wiederholen:

std::string get_string() {
    return {}; // an empty string is returned
}

return NULLund return nullptrsollte verwendet werden, wenn die Funktion einen Zeigertyp zurückgibt:

any_type* get_pointer() {
    return nullptr;
}

Jedoch NULLwird , da C ++ 11 veraltet , weil es nur ein Alias auf einen ganzzahligen Wert (0) ist, während nullptrein richtiger Zeigertyp ist:

int get_int() {
    return NULL; // will compile, NULL is an integer
}

int get_int() {
    return nullptr; // error: nullptr is not an integer
}
rgmt
quelle
91

Das ist wahrscheinlich verwirrend:

int foo()
{
  return {};   // honestly, just return 0 - it's clearer
}

Dies ist wahrscheinlich nicht:

SomeObjectWithADefaultConstructor foo()
{
  return {};
  // equivalent to return SomeObjectWithADefaultConstructor {};
}
Richard Hodges
quelle
9
Es ist also ein Fehler bei der Kompilierung, wenn der Rückgabetyp keinen Standardkonstruktor hat, richtig?
Pedia
10
Es ist ein Kompilierungsfehler, wenn der Rückgabetyp eine Klasse ist, die keinen nicht expliziten Standardkonstruktor hat und kein Aggregat ist.
Oktalist
3
Wenn der Typ einen initializer_listKonstruktor hat, würde dieser nicht verwendet, wenn kein Standardkonstruktor verfügbar ist?
Celtschk
4
"wahrscheinlich verwirrend"? Ist dies der Grund, warum sich eine unbenannte Seele auf "Diese aufgeblähte Obszönität, die C ++ ist" bezog? Kann das Einsparen von Tastenanschlägen möglicherweise das Potenzial für mangelnde Klarheit rechtfertigen? Dies ist eine aufrichtige Frage. Bitte überzeugen Sie mich mit praktischen Beispielen.
MickeyfAgain_BeforeExitOfSO
4
return {}ist NICHT gleichbedeutend mitreturn SomeObjectWithADefaultConstructor{};
MM
26

return {};bedeutet, dass dies {}der Initialisierer für den Rückgabewert ist . Der Rückgabewert wird mit einer leeren Liste listinitialisiert.


Hier einige Hintergrundinformationen zum Rückgabewert basierend auf [stmt.return] im C ++ - Standard:

Für eine Funktion, die nach Wert zurückgibt (dh der Rückgabetyp ist keine Referenz und nicht void), gibt es ein temporäres Objekt, das als Rückgabewert bezeichnet wird . Dieses Objekt wird von der returnAnweisung erstellt, und seine Initialisierer hängen davon ab, was in der return-Anweisung enthalten war.

Der Rückgabewert bleibt bis zum Ende des vollständigen Ausdrucks im Code erhalten, der die Funktion aufgerufen hat. Wenn es einen Klassentyp hat, wird sein Destruktor ausgeführt, es sei denn, der Aufrufer hat die Lebensdauer verlängert und einen Verweis direkt an ihn gebunden.

Der Rückgabewert kann auf zwei verschiedene Arten initialisiert werden:


Unter der Annahme , Tist der Rückgabetyp der Funktion, dann beachten Sie, dass return T{};zu unterschiedlich ist return {}: in der ehemaligen, eine temporäre T{}erstellt wird , und dann wird der Rückgabewert ist kopier initialisiert von diesem zeitlich begrenzt.

Dies kann nicht kompiliert werden, wenn Tkein zugänglicher Kopier- / Verschiebungskonstruktor vorhanden ist, ist jedoch auch return {};dann erfolgreich, wenn diese Konstruktoren nicht vorhanden sind. Dementsprechend return T{};können Nebenwirkungen des Kopierkonstruktors usw. auftreten, obwohl dies ein Kopierelisionskontext ist, so dass dies möglicherweise nicht der Fall ist.


Hier ist eine kurze Zusammenfassung der list-Initialisierung in C ++ 14 (N4140 [dcl.init.list] / 3), wobei der Initialisierer ist eine leere Liste:

  • Wenn Tes sich um ein Aggregat handelt, wird jedes Mitglied von seinem Klammer-oder-Gleich-Initialisierer initialisiert, falls es eines hatte, andernfalls wie von {} (wenden Sie diese Schritte also rekursiv an).
  • Wenn Tes sich um einen Klassentyp mit einem vom Benutzer bereitgestellten Standardkonstruktor handelt, wird dieser Konstruktor aufgerufen.
  • Wenn Tes sich um einen Klassentyp mit einem implizit definierten oder = defaulted-Standardkonstruktor handelt, wird das Objekt mit Null initialisiert und anschließend der Standardkonstruktor aufgerufen.
  • Wenn a Tist std::initializer_list, ist der Rückgabewert eine leere solche Liste.
  • Andernfalls (dh es Thandelt sich um einen Nichtklassentyp - Rückgabetypen können keine Arrays sein) wird der Rückgabewert mit Null initialisiert.
MM
quelle
Aggregate init steht an erster Stelle und initialisiert rekursiv jedes Mitglied mit {}, das möglicherweise value-init ist oder nicht.
TC
@TC richtig, ich ging durch cppreference aber übersah ein "bis C ++ 14"
MM
3

Es ist eine Art Kurzschrift für eine neue Instanz des Rückgabetyps "Methoden".

Victor Mwenda
quelle