Que signifie l'instruction «return {}» en C ++ 11?

115

Qu'est-ce que la déclaration

return {};

en C ++ 11 indiquer, et quand l'utiliser au lieu de (disons)

return NULL;

ou

return nullptr;
Pedia
la source
59
il renvoie une instance construite par défaut du type de retour de la fonction.
Richard Hodges le
Ou c'est simple return;sans valeur?
i486 le
Non, comme le révèle la discussion, c'est une erreur de compilation si votre fonction doit renvoyer quelque chose (c'est-à-dire pas de type de retour void) et que vous écrivez juste. return; D'autre part, return{};c'est valide si vous avez un type de retour.
Pedia le
@Pedia Pas toujours, certains objets nécessiteront des arguments pour construire
MM

Réponses:

108

return {};indique "retourner un objet du type de retour de la fonction initialisé avec un initialiseur de liste vide ". Le comportement exact dépend du type de l'objet retourné.

De cppreference.com (parce que l'OP est étiqueté C ++ 11, j'ai exclu les règles en C ++ 14 et C ++ 17; reportez-vous au lien pour plus de détails):

  • Si la liste-init-accolades est vide et que T est un type de classe avec un constructeur par défaut, l'initialisation de la valeur est effectuée.
  • Sinon, si T est un type d'agrégat, l'initialisation d'agrégat est effectuée.
  • Sinon, si T est une spécialisation de std :: initializer_list, l'objet T est initialisé directement ou par copie, en fonction du contexte, à partir de la liste d'initiation entre accolades.
  • Sinon, les constructeurs de T sont considérés, en deux phases:

    • Tous les constructeurs qui prennent std :: initializer_list comme seul argument, ou comme premier argument si les arguments restants ont des valeurs par défaut, sont examinés et mis en correspondance par résolution de surcharge contre un seul argument de type std :: initializer_list
    • Si l'étape précédente ne produit pas de correspondance, tous les constructeurs de T participent à la résolution des surcharges par rapport à l'ensemble d'arguments qui se compose des éléments de la liste braced-init-list, avec la restriction que seules les conversions non restrictives sont autorisées. Si cette étape produit un constructeur explicite comme meilleure correspondance pour une initialisation de liste de copie, la compilation échoue (notez que dans une simple initialisation de copie, les constructeurs explicites ne sont pas du tout considérés).
  • Sinon (si T n'est pas un type de classe), si la liste d'init-accolades n'a qu'un seul élément et que T n'est pas un type de référence ou est un type de référence compatible avec le type de l'élément, T est direct- initialisé (dans l'initialisation de la liste directe) ou initialisé par la copie (dans l'initialisation de la liste de copie), sauf que les conversions restreintes ne sont pas autorisées.

  • Sinon, si T est un type de référence qui n'est pas compatible avec le type de l'élément. (cela échoue si la référence est une référence non-const lvalue)
  • Sinon, si la liste d'initialisation entre accolades ne contient aucun élément, T est initialisé par valeur.

Avant C ++ 11, pour une fonction retournant a std::string, vous auriez écrit:

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

En utilisant la syntaxe d'accolade en C ++ 11, vous n'avez pas besoin de répéter le type:

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

return NULLet return nullptrdoit être utilisé lorsque la fonction renvoie un type de pointeur:

any_type* get_pointer() {
    return nullptr;
}

Cependant, NULLest déconseillé depuis C ++ 11 car il s'agit simplement d'un alias vers une valeur entière (0), alors qu'il nullptrs'agit d'un type de pointeur réel:

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

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

C'est probablement déroutant:

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

Ce n'est probablement pas:

SomeObjectWithADefaultConstructor foo()
{
  return {};
  // equivalent to return SomeObjectWithADefaultConstructor {};
}
Richard Hodges
la source
9
Donc, c'est une erreur de compilation si le type de retour n'a pas de constructeur par défaut, correct?
Pedia le
10
C'est une erreur de compilation si le type de retour est une classe qui n'a pas de constructeur par défaut non explicite et n'est pas un agrégat.
Oktalist le
3
Si le type a un initializer_listconstructeur, ne serait-il pas utilisé si aucun constructeur par défaut n'est disponible?
celtschk le
4
"probablement déroutant"? Est-ce la raison pour laquelle une âme anonyme s'est référée à "Cette obscénité gonflée qu'est C ++"? Quelle que soit l'économie de frappes que cela permet, est-ce que cela peut justifier le manque de clarté qu'il offre? C'est une question sincère. Veuillez me convaincre avec des exemples pratiques.
MickeyfAgain_BeforeExitOfSO
4
return {}n'est PAS équivalent àreturn SomeObjectWithADefaultConstructor{};
MM
26

return {};signifie que {}c'est l'initialiseur de la valeur de retour . La valeur de retour est initialisée par liste avec une liste vide.


Voici quelques informations générales sur la valeur de retour , basée sur [stmt.return] dans la norme C ++:

Pour une fonction qui renvoie par valeur (c'est-à-dire que le type de retour n'est pas une référence et non void), il existe un objet temporaire appelé valeur de retour . Cet objet est créé par l' returninstruction et ses initialiseurs dépendent de ce qui se trouvait dans l'instruction return.

La valeur de retour survit jusqu'à la fin de l'expression complète dans le code qui a appelé la fonction; s'il a un type de classe, alors son destructeur s'exécutera à moins qu'il n'ait prolongé sa durée de vie par l'appelant qui lui lie directement une référence.

La valeur de retour peut être initialisée de deux manières différentes:

  • return some_expression;- la valeur de retour est initialisée par copie à partir desome_expression
  • return { possibly_empty_list };- la valeur de retour est initialisée par liste à partir de la liste.

En supposant qu'il Ts'agit du type de retour de la fonction, notez qu'il return T{};est différent de return {}: dans le premier, un temporaire T{}est créé, puis la valeur de retour est initialisée par copie à partir de ce temporaire.

La compilation échouera si Tn'a pas de constructeur de copie / déplacement accessible, mais return {};réussira même si ces constructeurs ne sont pas présents. En conséquence, return T{};peut montrer des effets secondaires du constructeur de copie, etc., bien qu'il s'agisse d'un contexte d'élision de copie, donc il ne peut pas.


Voici un bref récapitulatif de l' initialisation de liste en C ++ 14 (N4140 [dcl.init.list] / 3), où l'initialiseur est une liste vide:

  • Si Test un agrégat, alors chaque membre est initialisé à partir de son initialiseur d' accolade ou d'égalité s'il en avait un, sinon comme par {} (appliquez donc ces étapes de manière récursive).
  • Si Test un type de classe avec un constructeur par défaut fourni par l'utilisateur, ce constructeur est appelé.
  • Si Test un type de classe avec un constructeur implicitement défini ou = defaulted par défaut, l'objet est initialisé à zéro , puis le constructeur par défaut est appelé.
  • Si Test a std::initializer_list, la valeur de retour est une telle liste vide.
  • Sinon (c'est-à T- dire est un type non-classe - les types de retour ne peuvent pas être des tableaux), la valeur de retour est initialisée à zéro.
MM
la source
Aggregate init vient en premier, et il initialise récursivement chaque membre avec {}, qui peut ou non être value-init.
TC
@TC à droite, je suis passé par cppreference mais j'ai oublié un "jusqu'au C ++ 14"
MM
3

C'est une sorte de raccourci pour une nouvelle instance du type de retour des méthodes.

Victor Mwenda
la source