En dehors du code générique (c'est-à-dire les modèles), vous pouvez (et je le fais) utiliser les accolades partout . L'un de ses avantages 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 des fonctions :
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, je n'accorde pas beaucoup d'attention à la différence entre les T t = { init };
o T t { init };
je trouve que la différence est mineure et qu'elle se traduira au pire par un message utile du compilateur sur l'utilisation abusive d'un style explicit
constructeur.
Pour les types qui acceptent les std::initializer_list
Bien qu'il soit évident que, parfois, le non std::initializer_list
sont nécessaires (l'exemple classique étant std::vector<int> twenty_answers(20, 42);
). Il n'y a donc pas de problème à ne pas utiliser d'accolades.
En ce qui concerne le code générique (c'est-à-dire dans les modèles), ce dernier paragraphe aurait dû susciter quelques mises en garde. Prenons l'exemple suivant :
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }
Dans ce cas auto p = make_unique<std::vector<T>>(20, T {});
crée un vecteur de taille 2 si T
est par exemple int
ou un vecteur de taille 20 si T
es std::string
. Un signe très révélateur que quelque chose ne tourne pas rond dans cette affaire est qu'il y a non qui peut vous sauver ici (par exemple avec SFINAE) : std::is_constructible
est en termes d'initialisation directe, alors que nous utilisons l'initialisation par accolade qui diffère de l'initialisation directe si et seulement si il n'y a pas de constructeur prenant std::initializer_list
interférer. De même std::is_convertible
n'est d'aucune utilité.
J'ai cherché à savoir s'il était possible de développer à la main une caractéristique permettant de résoudre ce problème, mais je ne suis pas très optimiste à ce sujet. Quoi qu'il en soit, je ne pense pas qu'il nous manquerait grand-chose, je pense que le fait que make_unique<T>(foo, bar)
se traduisent par une construction équivalente à T(foo, bar)
est très intuitif, surtout si l'on tient compte du fait que make_unique<T>({ foo, bar })
est assez différent et n'a de sens que si foo
y bar
ont le même type.
D'où pour le code générique, je n'utilise les accolades que pour l'initialisation des valeurs (par exemple T t {};
o T t = {};
), ce qui est très pratique et, à mon avis, supérieur à la méthode C++03 T t = T();
. Sinon, il s'agit d'une syntaxe d'initialisation directe (c'est-à-dire T t(a0, a1, a2);
), ou parfois une construction par défaut ( T t; stream >> t;
étant le seul cas où je l'utilise, je pense).
Cela ne signifie pas que tous Les accolades ne sont pas une bonne chose, comme le montre l'exemple précédent avec les 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)...) }; }
Il utilise toujours des accolades pour construire le std::unique_ptr<T>
même si le type réel dépend du paramètre du modèle T
.