web-dev-qa-db-fra.com

Quelles sont les différences entre C-like, constructeur et initialisation uniforme?

À 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?

52
dayuloli

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:

  1. Initialisez d'abord l'arr ci-dessus puis passez arr et arr + 4
  2. Créez le vecteur et Push_back () les éléments individuellement ou en boucle.

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.

59
nasser-sh

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.

  • s'il y a un constructeur prenant std::initializer_list, il utilise cela;
  • sinon, s'il y a un constructeur prenant un seul argument d'un type approprié, il l'utilise;
  • sinon, si c'est un agrégat (sans constructeurs) avec un membre, ce membre est initialisé avec zéro;
  • sinon, c'est une erreur.

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".

15
Mike Seymour