Que signifie “return {}” dans C ++ 11?

Qu’est-ce que la déclaration

return {}; 

dans C ++ 11 indiquer, et quand l’utiliser à la place de (dire)

 return NULL; 

ou

 return nullptr; 

return {}; indique “renvoie un object du type de retour de la fonction initialisé avec un initialiseur de liste vide”. Le comportement exact dépend du type de l’object renvoyé.

Depuis cppreference.com (car l’OP est marqué C ++ 11, j’ai exclu les règles de C ++ 14 et C ++ 17; reportez-vous au lien pour plus de détails):

  • Si la liste d’initialisation braced 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 globale est effectuée.
  • Sinon, si T est une spécialisation de std :: initializer_list, l’object T est initialisé ou initialisé par copie, selon le contexte, à partir de la liste braced-init.
  • 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 comparés par une résolution de surcharge à 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 constitués des éléments de la liste initiée, avec la ressortingction que seules les conversions non ressortingctives sont autorisées. Si cette étape produit un constructeur explicite correspondant le mieux à une initialisation de liste de copie, la compilation échoue (notez que dans une simple initialisation de copie, les constructeurs explicites ne sont pas pris en compte).
  • Sinon (si T n’est pas un type de classe), si la liste-initiée-braced n’a qu’un é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 par liste directe) ou initialisé par copie (dans l’initialisation de la liste de copie), sauf que les conversions ressortingctives 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. (ceci échoue si la référence est une référence non constante)
  • Dans le cas contraire, si la liste-initié-braced n’a pas d’éléments, T est initialisé en valeur.

Avant C ++ 11, pour une fonction renvoyant un std::ssortingng , vous auriez écrit:

 std::ssortingng get_ssortingng() { return std::ssortingng(); } 

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

 std::ssortingng get_ssortingng() { return {}; // an empty ssortingng is returned } 

return NULL et return nullptr doit être utilisé lorsque la fonction retourne un type de pointeur:

 any_type* get_pointer() { return nullptr; } 

Cependant, NULL est déconseillé depuis C ++ 11 car il s’agit simplement d’un alias à une valeur entière (0), alors que nullptr est un type de pointeur réel:

 int get_int() { return NULL; // will comstack, NULL is an integer } int get_int() { return nullptr; // error: nullptr is not an integer } 

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 {}; } 

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


Voici quelques informations sur la valeur de retour , basée sur [stmt.return] dans le standard C ++:

Pour une fonction qui renvoie par valeur (c’est-à-dire que le type de retour n’est pas une référence et n’est pas void ), il existe un object temporaire appelé la valeur de retour . Cet object est créé par l’instruction return et ses initialiseurs dépendent de ce qui était 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, son destructeur s’exécutera à moins qu’il ait une durée de vie étendue par l’appelant lui liant 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 à partir de some_expression
  • return { possibly_empty_list }; – la valeur de retour est initialisée à partir de la liste.

En supposant que T est le type de retour de la fonction, notez alors que return T{}; est différent de return {} : dans le premier cas, un T{} temporaire est créé, puis la valeur de retour est initialisée à partir de ce temporaire.

Cela échouera à comstackr si T n’a pas de constructeur accessible par copie / déplacement, 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, alors ce n’est peut-être pas le cas.


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

  • Si T est un agrégat, alors chaque membre est initialisé à partir de son initialisateur d’ accolade ou égal s’il en avait un, sinon comme par {} (donc appliquez ces étapes de manière récursive).
  • Si T est un type de classe avec un constructeur par défaut fourni par l’utilisateur, ce constructeur est appelé.
  • Si T est un type de classe avec un constructeur implicite défini implicitement ou = default défaut, l’object est initialisé à zéro et le constructeur par défaut est appelé.
  • Si T est un std::initializer_list , la valeur de retour est une liste vide.
  • Sinon (c’est-à-dire que T est un type non-classe – les types de retour ne peuvent pas être des tableaux), la valeur de retour est initialisée à zéro.

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