Si je me fie à la documentation sur cppreference.com (version anglaise), il y a 9 types d'initialisation.
Alors pour un int, je pense que tu as cité toutes les méthodes raisonnablement possibles, à l'exception de l'initialisation par défaut (int i;
) qu'il faut éviter pour les types tels que ìnt
pour éviter de se retrouver avec une variable non initialisé.
En revanche, pour les objets, la sémantique est bien plus complexe. Il faut d'abord savoir que std::string s();
ne fait pas du tout ce à quoi on peut s'attendre au premier abord. En fait, la syntaxe est ambigüe puisqu'il peut s'agir soit de la déclaration d'une variable de type std::string
en ne donnant aucun argument au constructeur, soit de la déclaration d'une fonction s
qui retourne un std::string
et qui ne prend aucun argument. C'est cette deuxième possibilité qui est utilisé dans les spécifications. Ce problème arrive aussi pour des trucs un poil plus subtils : std::string s(std::string());
. Encore une fois, c'est ambigüe puisqu'il peut s'agir soit d'une déclaration de fonction, soit d'une déclaration de variable.
Il est possible de résoudre cette ambigüité en ajoutant des parenthèses supplémentaires : std::string s((std::string()));
défini toujours une variable et jamais une fonction.
Pour un objet, la plupart du temps std::string s;
et std::string s{};
auront le même effet et appellent le constructeur par défaut. std::string s();
ne fait pas ce que tu veux.
std::string s("hello")
appel le constructeur de std::string
qui prend en paramètre un char*
. Note, c'est dans ce cas qu'il faut faire attention aux possibles ambigüités.
std::string s = "hello";
vérifie que "hello"
peut se convertir en std::string
, fait la conversion et utilise le constructeur par copie de std::string
pour initialiser s
. Pour le coup, c'est très souvent directement optimisé par le compilateur pour donner les mêmes performances que std::string s("hello");
.
std::string s{"hello"}
est une syntaxe qui permet d'initialiser s
avec une valeur. Pour une chaîne de caractère, on ne voit pas trop la différence avec std::string s("hello")
mais avec un type comme std::vector
, c'est beaucoup plus net.
std::vector<int> v(1, 2, 3, 4, 5);
ne fonctionne pas : il n'y a pas de constructeur qui prend en paramètre 5 entiers. En revanche std::vector<int> v{1, 2, 3, 4, 5};
fonctionne en appelant le constructeur de std::vector
qui prend en paramètre une liste d'initialisation (ajouté en C++11).
Si la liste d'initialisation ne convient pas (par exemple pour std::vector<int> v2{v1.begin(), v1.end()};
), le compilateur utilise le constructeur qui convient (comme si on utilisait des parenthèses).
On pourrait donc en conclure qu'utiliser les accolades permet d'éviter les ambigüités et fait au moins autant que les parenthèses, mais ce n'est pas exacte. En effet std::vector<int> v(10);
créé un vecteur de 10 int, alors que std::vector<int> v{10};
créé un vecteur avec un seul int (qui vaut 10).
Bon, j'espère que je n'ai pas fait d'erreur et que j'ai été assez clair sur ce qui fonctionne et ce qui ne fonctionne pas.