MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Pourquoi ?
MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Pourquoi ?
Il s'agit essentiellement d'un copier-coller de l'ouvrage de Bjarne Stroustrup intitulé "Le langage de programmation C++ 4e édition" :
Initialisation de la liste ne permet pas le rétrécissement (§iso.8.5.4). C'est-à-dire que
Exemple :
void fun(double val, int val2) {
int x2 = val; // if val == 7.9, x2 becomes 7 (bad)
char c2 = val2; // if val2 == 1025, c2 becomes 1 (bad)
int x3 {val}; // error: possible truncation (good)
char c3 {val2}; // error: possible narrowing (good)
char c4 {24}; // OK: 24 can be represented exactly as a char (good)
char c5 {264}; // error (assuming 8-bit chars): 264 cannot be
// represented as a char (good)
int x4 {2.0}; // error: no double to int value conversion (good)
}
En sólo La situation dans laquelle = est préféré à {} est celle où l'on utilise auto
pour obtenir le type déterminé par l'initialisateur.
Exemple :
auto z1 {99}; // z1 is an int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99; // z3 is an int
Préférez l'initialisation {} aux alternatives, à moins que vous n'ayez une bonne raison de ne pas le faire.
Il y a aussi le fait qu'utiliser ()
peut être analysé comme une déclaration de fonction. Il est confus et incohérent que vous puissiez dire T t(x,y,z);
mais pas T t()
. Et parfois, vous êtes certain x
tu ne peux même pas dire T t(x);
.
Je ne suis pas du tout d'accord avec cette réponse ; l'initialisation entre accolades devient un désordre complet quand vous avez des types avec un ctor acceptant un std::initializer_list
. RedXIII mentionne cette question (et la balaie d'un revers de main), alors que vous l'ignorez complètement. A(5,4)
y A{5,4}
peuvent appeler des fonctions complètement différentes, et c'est une chose importante à savoir. Cela peut même donner lieu à des appels qui semblent peu intuitifs. En disant que vous devriez préférer {}
par défaut, les gens comprendront mal ce qui se passe. Ce n'est pas votre faute, cependant. Je pense personnellement que c'est une fonctionnalité extrêmement mal pensée.
@user1520427 C'est pourquoi il y a le " sauf si vous avez une bonne raison de ne pas le faire partie ".
Il y a déjà d'excellentes réponses sur les avantages de l'utilisation de l'initialisation de liste, mais ma règle personnelle est de NE PAS utiliser d'accolades chaque fois que cela est possible, mais plutôt de le faire dépendre de la signification conceptuelle :
D'après mon expérience, cet ensemble de règles peut être appliqué de manière beaucoup plus cohérente que l'utilisation des accolades par défaut, mais en devant se souvenir explicitement de toutes les exceptions lorsqu'elles ne peuvent pas être utilisées ou ont une signification différente de la syntaxe "normale" d'appel de fonction avec parenthèses (appelle une surcharge différente).
Il s'adapte bien, par exemple, aux types de bibliothèque standard tels que std::vector
:
vector<int> a{10, 20}; //Curly braces -> fills the vector with the arguments
vector<int> b(10, 20); //Parentheses -> uses arguments to parametrize some functionality,
vector<int> c(it1, it2); //like filling the vector with 10 integers or copying a range.
vector<int> d{}; //empty braces -> default constructs vector, which is equivalent
//to a vector that is filled with zero elements
Il existe de NOMBREUSES raisons d'utiliser l'initialisation en accolade, mais vous devez savoir que le site initializer_list<>
est préféré aux autres constructeurs à l'exception du constructeur par défaut. Cela entraîne des problèmes avec les constructeurs et les modèles où le type T
Le constructeur peut être soit une liste d'initialisateurs, soit un simple ctor.
struct Foo {
Foo() {}
Foo(std::initializer_list<Foo>) {
std::cout << "initializer list" << std::endl;
}
Foo(const Foo&) {
std::cout << "copy ctor" << std::endl;
}
};
int main() {
Foo a;
Foo b(a); // copy ctor
Foo c{a}; // copy ctor (init. list element) + initializer list!!!
}
En supposant que vous ne rencontriez pas de telles classes, il y a peu de raisons de ne pas utiliser la liste d'initialisation.
Il s'agit d'un très point important de la programmation générique. Lorsque vous écrivez des modèles, no utiliser des listes d'initialisation entre crochets (nom donné par la norme à l'expression { ... }
), à moins que vous ne souhaitiez que le initializer_list
sémantique (enfin, et peut-être pour la construction par défaut d'un objet).
Honnêtement, je ne comprends pas pourquoi le std::initializer_list
La règle n'existe même pas - elle ne fait qu'ajouter de la confusion et du désordre au langage. Qu'y a-t-il de mal à faire Foo{{a}}
si vous voulez que le std::initializer_list
constructeur ? Cela semble beaucoup plus facile à comprendre que d'avoir std::initializer_list
ont la priorité sur toutes les autres surcharges.
+1 pour le commentaire ci-dessus, car c'est vraiment le bordel je pense ! !! ce n'est pas logique ; Foo{{a}}
suit une certaine logique pour moi bien plus que Foo{a}
qui se transforme en une préséance de la liste des initiales (l'utilisateur pourrait penser hm...)
Mise à jour (2022-02-11) : Notez qu'il existe des opinions plus récentes à ce sujet que celle postée à l'origine (ci-dessous), qui plaident contre la préférence de l'initialisateur {}, comme par exemple Arthur Dwyer dans son billet de blog sur Le cauchemar de l'initialisation en C++ .
Réponse originale :
Lire Herb Sutter's (mis à jour) GotW #1 . Ceci explique en détail la différence entre ces options, et quelques autres, ainsi que plusieurs problèmes pertinents pour distinguer le comportement des différentes options.
Le gist/copié de la section 4 :
Quand faut-il utiliser la syntaxe ( ) ou { } pour initialiser des objets ? Et pourquoi ? Voici une directive simple :
Ligne directrice : Préférez utiliser l'initialisation avec { }, comme le vecteur v = { 1, 2, 3, 4 } ; ou auto v = vector{ 1, 2, 3, 4 } ;, car car c'est plus cohérent, plus correct et évite d'avoir à connaître les pièges des les pièges de l'ancien style. Dans les cas à argument unique où vous préférez voir uniquement le signe =, comme int i = 42 ; et auto x = n'importe quoi ; l'omission des accolades ne pose aucun problème.
Cela couvre la grande majorité des cas. Il n'y a qu'une seule grande exception :
Dans de rares cas, comme le vecteur v(10,20) ; ou auto v = vecteur(10,20) ;, utilisez l'initialisation avec ( ) pour appeler explicitement un constructeur qui est autrement caché par un constructeur initializer_list constructeur.
Cependant, la raison pour laquelle cela devrait être généralement " rare " est que les défauts et la construction par copie sont déjà spéciales et fonctionnent bien avec { }, et que une bonne conception de classe évite maintenant la plupart du temps le cas de recours à( ) pour les constructeurs définis par l'utilisateur. constructeurs définis par l'utilisateur grâce à cette directive finale de conception :
Ligne directrice : Quand vous concevez une classe, évitez de fournir un constructeur qui surcharge de manière ambiguë avec un constructeur initializer_list, afin que les utilisateurs n'auront pas besoin d'utiliser ( ) pour atteindre un tel constructeur caché.
Voir également les directives de base à ce sujet : ES.23 : Préférer la syntaxe {}-initialisateur .
C'est seulement plus sûr tant que vous ne construisez pas avec -Wno-narrowing comme le fait Google dans Chromium. Si vous le faites, alors il est MOINS sûr. Sans ce drapeau, les seuls cas non sûrs seront corrigés par C++20.
Note : A) Les parenthèses bouclées sont plus sûres car elles ne permettent pas de rétrécir. B) Les accolades sont moins sûres car elles permettent de contourner les constructeurs privés ou supprimés, et d'appeler implicitement les constructeurs marqués explicites.
Ces deux éléments combinés signifient qu'ils sont plus sûrs si ce qui est à l'intérieur est une constante primitive, mais moins sûrs s'il s'agit d'objets (bien que cela ait été corrigé en C++20).
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
13 votes
Pourquoi ne pas utiliser
auto
?1 votes
@MarkGarcia Huh ? Veuillez préciser :)
4 votes
Cela n'irait pas dans la boîte à commentaires ;). Quoi qu'il en soit, pour citer l'article en question : "...les principales raisons de déclarer des variables en utilisant auto sont la correction, la performance, la maintenabilité et la robustesse - et, oui, la commodité..." .
59 votes
C'est vrai, c'est pratique, mais cela réduit la lisibilité à mon avis - j'aime bien voir le type d'un objet lors de la lecture du code. Si vous êtes 100% sûr du type de l'objet, pourquoi utiliser auto ? Et si vous utilisez l'initialisation par liste (lisez ma réponse), vous pouvez être sûr qu'elle est toujours correcte.
161 votes
@Oleksiy :
std::map<std::string, std::vector<std::string>>::const_iterator
aimerait vous parler.12 votes
@Oleksiy Je recommande de lire ce GotW .
2 votes
@MSalters : Eh, vous pouvez déjà le faire sans
auto
. Bien sûr, il pourrait s'agir d'un "coup de gueule qui ne devrait pas être arrêté par les faits" ;)5 votes
@Xeo
typedef std::map<std::string, std::vector<std::string>> MyContainer
est généralement une alternative plus propre. J'utiliseauto
pour les types de portée locale uniquement.25 votes
@doc je dirais
using MyContainer = std::map<std::string, std::vector<std::string>>;
est encore mieux (d'autant plus que vous pouvez le modeler !)1 votes
Le premier et le second ne sont pas exactement équivalents. La première est une initialisation par liste directe, la seconde est une initialisation par liste de copie. Voir fr.cppreference.com/w/cpp/language/list_initialization
0 votes
Duplicata possible : stackoverflow.com/q/9976927/3560202 ?
0 votes
Voir aussi isocpp.github.io/CppCoreGuidelines/
1 votes
Juste un ajout tardif : "presque toujours auto" fait un Graal de - ce qu'il est. no ! !!. Il y a de bonnes raisons d'utiliser auto (l'exemple de l'itérateur ci-dessus), mais dans d'autres situations, il vaut mieux no utilisé. Exemples :
2 votes
unsigned int n = 7;
vs.auto n = 7U;
- il est tout simplement trop facile d'oublier le suffixe (ce qui s'est produit dans la première variante, intentionnellement, à des fins de démonstration, cependant, la première variante est robuste contre), résultant enn
être de mauvais type. Encore pire :auto n = 7UL;
où l'on veut imposer que n soit de typeuint64_t
- et BAM, on n'est pas sur linux 64bit et on obtient justeuint32_t
à la place (ou peut-être même pas celui-ci, caruint32_t
pourrait être défini comme suitunsigned int
sur le système actuel et vous pourriez vous retrouver, par exemple, avec des pointeurs incompatibles).9 votes
Mon Dieu, il n'y a que dans les C++ que des questions comme celle-là existent. Merci beaucoup d'avoir posé cette question, les réponses m'ont vraiment aidé.
0 votes
@CoffeeTableEspresso, santé, mon frère ! Espérons que le Guide apocryphe de la théologie de l'initialisation du C++ sera publié prochainement.