À ma connaissance, il existe trois façons d'initialiser une variable en C++.
int x = 0; // C-like initialization
int x (0); // Constructor initialization
int x {0}; // Uniform initialization
L'initialisation uniforme a été effectuée pour C++ 11 afin de fournir une syntaxe plus uniforme pour l'initialisation de différents types de variables, ce qui nécessitait une syntaxe différente dans C++ .
Quelles sont les différences entre C-like, constructeur et initialisation uniforme? Et dois-je toujours utiliser l'initialisation uniforme?
Tout d'abord, je recommanderais de regarder le discours suivant par Herb Sutter, dans lequel il donne quelques recommandations sur le sujet. La discussion d'initialisation de l'accolade commence à vers 23h .
Lorsque vous parlez de types de données primitifs, les 3 donnent le même résultat. Personnellement, je préfère m'en tenir à l'ancien int x = 0
la syntaxe, mais elle se résume à des préférences personnelles.
Pour les types de classe, l'initialisation d'accolade et l'initialisation du constructeur old-school ne sont pas complètement interchangeables. Par exemple:
vector<int> v (100); // Creates a 100-element vector
vector<int> v {100}; // Creates a 1-element vector, holding the value 100.
Ceci est dû au fait std::vector
a un constructeur qui définit explicitement std::initializer_list
comme seul argument. Garde en tête que
auto var = {1, 2};
crée un std::initializer_list
, avec var
comme identifiant.
La chose à propos des listes d'initialisation est qu'elles fournissent une cohérence qui est un changement bienvenu par rapport à ce qui était disponible auparavant. Par exemple, si vous deviez initialiser un tableau en C++, vous utiliseriez:
int arr[] = {1, 2, 3, 4};
Mais si vous vouliez initialiser un vector<int>
avec les mêmes éléments, il fallait soit:
arr
et arr + 4
Avec C++ 11, vous pouvez simplement utiliser
vector<int> v = {1, 2, 3, 4}; // Same syntax. Nice! Note that the = is optional
Une autre instance dans laquelle l'initialisation d'accolade est utile est qu'elle fournit une solution de contournement à C++ analyse la plus vexante . D'après l'exposé, supposons que nous avons deux classes, Origin
et extents
, dont les instances peuvent être passées pour construire un autre objet de type rectangle
. La déclaration suivante:
rectangle w(Origin(), extents());
ne vous permet pas de créer un objet rectangle
à l'aide de Origin
et extents
temporaires, car cette instruction est analysée comme une déclaration de fonction. TTT ... TTT. Donc normalement, vous devriez faire:
Origin o;
extents e;
rectangle w(o, e);
Avec l'initialisation de l'accolade, vous pouvez les créer à la volée et
rectangle w {Origin(), extents()};
fonctionnera comme prévu, c'est-à-dire transmis au constructeur qui est surchargé avec un objet Origin
comme premier argument et un objet extents
comme deuxième.
La règle est pour les objets, utilisez l'initialisation d'accolade sauf si vous avez une raison de ne pas le faire.
Quelles sont les différences entre c-like, constructeur et initialisation uniforme?
Pour les types primitifs comme int
, il n'y a pas de différence pratique; considérons donc un type de classe T
à la place.
Le premier style équivaut à
T x(T(0));
créer un objet temporaire à partir de l'expression d'initialisation, puis initialiser x
en le déplaçant ou en le copiant. En pratique, le déplacement ou la copie sera élidé, de sorte que le résultat soit le même que le deuxième style; la seule différence est que le premier échouera s'il n'y a pas de constructeur de copie ou de déplacement accessible.
La seconde initialise directement l'objet à l'aide d'un constructeur qui prend un argument, donnant une erreur s'il n'y a pas de constructeur approprié.
Le troisième dépend des constructeurs disponibles.
std::initializer_list
, il utilise cela;Et dois-je toujours utiliser l'initialisation uniforme?
Non. Parfois, vous avez besoin d'une initialisation de style fonction pour distinguer un initializer_list
constructeur et un prenant d'autres types d'arguments. Par exemple:
std::vector<int> v1(10, 42); // 10 elements with value 42
std::vector<int> v2{10, 42}; // 2 elements with values 10 and 42
Vous ne devriez pas non plus l'appeler "initialisation uniforme" car ce n'est pas "uniforme" dans un sens. Le terme officiel est "initialisation par accolade".