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:
struct foo {
// Ok
std::string a = { "foo" };
// Also ok
std::string b { "bar" };
// Not possible
std::string c("qux");
// For completeness this is possible
std::string d = "baz";
};
ou pour les arguments de fonction:
void foo(std::pair<int, double*>);
foo({ 42, nullptr });
// Not possible with parentheses without spelling out the type:
foo(std::pair<int, double*>(42, nullptr));
Pour les variables auxquelles je ne prête pas beaucoup d'attention entre les styles T t = { init };
ou T 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'un explicit
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 étant std::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:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }
auto p = make_unique<std::vector<T>>(20, T {});
Crée ensuite un vecteur de taille 2 si T
est par exemple int
, ou un vecteur de taille 20 si T
est std::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 constructeur std::initializer_list
n'interfère. De même std::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 cela make_unique<T>({ foo, bar })
est assez différent et n'a de sens que si foo
et bar
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 {};
ou T t = {};
), ce qui est très pratique et je pense supérieur à la méthode C ++ 03 T t = T();
. Sinon, c'est soit la syntaxe d'initialisation directe (ie T 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:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }
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èle T
.