En C ++ 11, nous avons cette nouvelle syntaxe pour l'initialisation des classes qui nous donne un grand nombre de possibilités pour initialiser des variables.
{ // Example 1
int b(1);
int a{1};
int c = 1;
int d = {1};
}
{ // Example 2
std::complex<double> b(3,4);
std::complex<double> a{3,4};
std::complex<double> c = {3,4};
auto d = std::complex<double>(3,4);
auto e = std::complex<double>{3,4};
}
{ // Example 3
std::string a(3,'x');
std::string b{3,'x'}; // oops
}
{ // Example 4
std::function<int(int,int)> a(std::plus<int>());
std::function<int(int,int)> b{std::plus<int>()};
}
{ // Example 5
std::unique_ptr<int> a(new int(5));
std::unique_ptr<int> b{new int(5)};
}
{ // Example 6
std::locale::global(std::locale("")); // copied from 22.4.8.3
std::locale::global(std::locale{""});
}
{ // Example 7
std::default_random_engine a {}; // Stroustrup's FAQ
std::default_random_engine b;
}
{ // Example 8
duration<long> a = 5; // Stroustrup's FAQ too
duration<long> b(5);
duration<long> c {5};
}
Pour chaque variable que je déclare, je dois réfléchir à la syntaxe d'initialisation que je dois utiliser et cela ralentit ma vitesse de codage. Je suis sûr que ce n'était pas l'intention d'introduire les accolades.
En ce qui concerne le code de modèle, la modification de la syntaxe peut avoir des significations différentes, il est donc essentiel de suivre la bonne voie.
Je me demande s'il existe une directive universelle sur la syntaxe à choisir.
c++
c++11
initializer-list
Helami
la source
la source
Réponses:
Je pense que ce qui suit pourrait être une bonne ligne directrice:
Si la valeur (unique) avec laquelle vous initialisez est destinée à être la valeur exacte de l'objet, utilisez l'
=
initialisation de copy ( ) (car en cas d'erreur, vous n'invoquerez jamais accidentellement un constructeur explicite, qui interprète généralement la valeur fournie différemment). Dans les endroits où l'initialisation de la copie n'est pas disponible, voyez si l'initialisation d'accolades a la sémantique correcte, et si c'est le cas, utilisez-la; sinon utilisez l'initialisation entre parenthèses (si cela n'est pas non plus disponible, vous n'avez pas de chance de toute façon).Si les valeurs avec lesquelles vous initialisez sont une liste de valeurs à stocker dans l'objet (comme les éléments d'un vecteur / tableau ou d'une partie réelle / imaginaire d'un nombre complexe), utilisez l'initialisation d'accolades si disponible.
Si les valeurs avec lesquelles vous initialisez ne sont pas des valeurs à stocker, mais décrivent la valeur / l'état prévu de l'objet, utilisez des parenthèses. Les exemples sont l'argument de taille d'un
vector
ou l'argument de nom de fichier d'unfstream
.la source
T {}
ou des raisons syntaxiques comme l' analyse la plus vexante ), mais en général, je pense que cela fait un bon conseil. Notez que c'est mon opinion subjective, donc on devrait aussi jeter un œil aux autres réponses.type var{};
Est-ce que.Je suis convaincu qu'il n'y aura jamais de directive universelle. Mon approche est d'utiliser toujours des accolades bouclées en se souvenant que
Ainsi, les accolades rondes et bouclées ne sont pas interchangeables. Mais savoir où ils diffèrent me permet d'utiliser l'initialisation entre crochets bouclés dans la plupart des cas (certains des cas où je ne peux pas sont actuellement des bogues du compilateur).
la source
int i = 0;
je ne pense pas que quiconque utiliseraitint i{0}
là-bas, et cela pourrait être déroutant (aussi,0
c'est si typeint
, donc il n'y aurait pas de rétrécissement ). Pour tout le reste, je suivrais les conseils de Juancho: préférez {}, méfiez-vous des quelques cas où vous ne devriez pas. Notez qu'il n'y a pas beaucoup de types qui prendront des listes d'initialiseurs comme arguments de constructeur, vous pouvez vous attendre à ce que les conteneurs et les types de type conteneur (tuple ...) les aient, mais la plupart du code appellera le constructeur approprié.int i{some floating point}
c'est une erreur, plutôt que de tronquer silencieusement.{}
pour signifier "initialiser" à moins que vous ne puissiez absolument pas .En dehors du code générique (c'est-à-dire des modèles), vous pouvez (et je le fais) utiliser des accolades partout . Un avantage est qu'il fonctionne partout, par exemple même pour l'initialisation en classe:
ou pour les arguments de fonction:
Pour les variables auxquelles je ne prête pas beaucoup d'attention entre les styles
T t = { init };
ouT t { init };
, je trouve que la différence est mineure et ne résultera au pire qu'en un message utile du compilateur sur une mauvaise utilisation d'unexplicit
constructeur.Pour les types qui acceptent
std::initializer_list
bien que de toute évidence, les non-std::initializer_list
constructeurs sont parfois nécessaires (l'exemple classique étantstd::vector<int> twenty_answers(20, 42);
). C'est bien de ne pas utiliser d'accolades alors.En ce qui concerne le code générique (c'est-à-dire dans les modèles), ce tout dernier paragraphe aurait dû soulever quelques avertissements. Considérer ce qui suit:
auto p = make_unique<std::vector<T>>(20, T {});
Crée ensuite un vecteur de taille 2 siT
est par exempleint
, ou un vecteur de taille 20 siT
eststd::string
. Un signe très révélateur qu'il se passe quelque chose de très mal ici est qu'il n'y a aucun trait qui peut vous sauver ici (par exemple avec SFINAE):std::is_constructible
c'est en termes d'initialisation directe, alors que nous utilisons l'initialisation d'accolades qui reporte à direct- initialisation si et seulement si aucun constructeurstd::initializer_list
n'interfère. De mêmestd::is_convertible
n'est d'aucune aide.J'ai cherché s'il était en fait possible de lancer manuellement un trait qui peut résoudre ce problème, mais je ne suis pas trop optimiste à ce sujet. En tout cas je ne pense pas qu'il nous manquerait grand-chose, je pense que le fait qu'il en
make_unique<T>(foo, bar)
résulte une construction équivalente àT(foo, bar)
est très intuitif; d'autant plus que celamake_unique<T>({ foo, bar })
est assez différent et n'a de sens que sifoo
etbar
ont le même type.Par conséquent, pour le code générique, je n'utilise que des accolades pour l'initialisation de la valeur (par exemple
T t {};
ouT t = {};
), ce qui est très pratique et je pense supérieur à la méthode C ++ 03T t = T();
. Sinon, c'est soit la syntaxe d'initialisation directe (ieT t(a0, a1, a2);
), soit parfois la construction par défaut (T t; stream >> t;
étant le seul cas où j'utilise cela je pense).Cela ne signifie pas que toutes les accolades sont mauvaises, considérez l'exemple précédent avec des correctifs:
Cela utilise toujours des accolades pour construire le
std::unique_ptr<T>
, même si le type réel dépend du paramètre de modèleT
.la source
make_unique<T>(20u, T {})
pourT
être soitunsigned
oustd::string
. Pas trop sûr des détails. (Notez que j'ai également commenté les attentes concernant l'initialisation directe par rapport à l'initialisation d'accolades concernant par exemple les fonctions de transfert parfait.)std::string c("qux");
N'a pas été spécifié pour fonctionner comme une initialisation en classe pour éviter les ambiguïtés avec les déclarations de fonctions membres dans la grammaire.