Qu'est-ce que la déclaration
return {};
en C++ 11 indiquer, et quand l'utiliser au lieu de (par exemple)
return NULL;
ou
return nullptr;
return {};
indique "retourne un objet du type de retour de la fonction initialisé avec un list-initializer vide". Le comportement exact dépend du type de l'objet renvoyé.
De cppreference.com (étant donné 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 initialisée est vide et que T est un type de classe avec un constructeur par défaut, une initialisation de valeur est effectuée.
- Sinon, si T est un type d'agrégat, l'initialisation de l'agrégat est effectuée.
- Sinon, si T est une spécialisation de std :: initializer_list, l'objet T est initialisé directement ou copié, en fonction du contexte, à partir de la liste initialisée.
Sinon, les constructeurs de T sont considérés, en deux phases:
- Tous les constructeurs qui utilisent 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 une résolution de surcharge par rapport à 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 de la surcharge par rapport à l'ensemble des arguments constitués des éléments de la liste initiée par les hachures, avec la restriction que seules les conversions sans limitation 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 l'initialisation de copie simple, les constructeurs explicites ne sont pas pris en compte).
Sinon (si T n'est pas un type de classe), si la liste dressée-dressée ne comporte 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é (en initialisation de liste directe) ou en copie initialisée (en initialisation de copie de liste), sauf que les conversions restrictives ne sont pas autorisées.
- Sinon, si T est un type de référence incompatible avec le type de l'élément. (cela échoue si la référence est une référence non constante de lvalue)
- Sinon, si la liste initialisée ne contient aucun élément, T est initialisé par la valeur.
Avant C++ 11, pour une fonction renvoyant un std::string
, vous auriez écrit:
std::string get_string() {
return std::string();
}
En utilisant la syntaxe d'accolade dans C++ 11, vous n'avez pas besoin de répéter le type:
std::string get_string() {
return {}; // an empty string is returned
}
return NULL
et return nullptr
devrait être utilisé lorsque la fonction renvoie un type de pointeur:
any_type* get_pointer() {
return nullptr;
}
Cependant, NULL
est obsolète depuis C++ 11 car il ne s'agit que d'un alias pour une valeur entière (0), tandis que nullptr
est 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
}
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 de la valeur de retour . La valeur de retour est initialisée par liste avec une liste vide.
Voici un aperçu de la valeur de retour , basée sur [stmt.return] dans la norme C++:
Pour une fonction qui retourne par valeur (c'est-à-dire que le type de retour n'est pas une référence et pas void
), il existe un objet temporaire appelé valeur de retour . Cet objet est créé par l'instruction return
et ses initialiseurs dépendent du contenu de l'instruction return.
La valeur de retour survit jusqu'à la fin de l'expression complète dans le code qui a appelé la fonction. si elle a un type de classe, son destructeur s'exécutera sauf si sa durée de vie est prolongée 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 copie initialisée de some_expression
return { possibly_empty_list };
- la valeur de retour est list-initialized de la liste.En supposant que T
est le type de retour de la fonction, notez que return T{};
est différent de return {}
: dans le premier cas, un T{}
est créé, puis la valeur de retour est copiée à partir de ce temporaire.
La compilation échouera si T
n’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 les effets secondaires du constructeur de copie, etc., bien qu'il s'agisse d'un contexte d'élision de copie.
Voici un bref récapitulatif de initialisation de liste en C++ 14 (N4140 [dcl.init.list]/3), où l'initialiseur est vide liste:
T
est un agrégat, chaque membre est initialisé à partir de son initialiseur (ou = égal) s'il en avait un, sinon si par {}
(appliquez donc ces étapes de manière récursive).T
est un type de classe avec un constructeur par défaut fourni par l'utilisateur, ce constructeur est appelé.T
est un type de classe avec une définition implicite, ou = default
ed constructeur par défaut, l'objet est initialisé à zéro , puis le constructeur par défaut est appelé.T
est un std::initializer_list
, la valeur de retour est une liste vide de ce type.T
est un type non-classe - les types de résultats 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 de méthodes.