Quand utiliser l’initialiseur fermé par accolade?

En C ++ 11, nous avons cette nouvelle syntaxe pour l’initialisation des classes, ce qui nous donne un grand nombre de possibilités pour initialiser les variables.

{ // Example 1 int b(1); int a{1}; int c = 1; int d = {1}; } { // Example 2 std::complex b(3,4); std::complex a{3,4}; std::complex c = {3,4}; auto d = std::complex(3,4); auto e = std::complex{3,4}; } { // Example 3 std::ssortingng a(3,'x'); std::ssortingng b{3,'x'}; // oops } { // Example 4 std::function a(std::plus()); std::function b{std::plus()}; } { // Example 5 std::unique_ptr a(new int(5)); std::unique_ptr 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 a = 5; // Stroustrup's FAQ too duration b(5); duration c {5}; } 

Pour chaque variable que je déclare, je dois penser à la syntaxe d’initialisation que je devrais 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 donner lieu à différentes significations.

Je me demande s’il existe une directive universelle à choisir.

    Je pense que ce qui suit pourrait être une bonne ligne direcsortingce:

    • Si la valeur (unique) avec laquelle vous effectuez l’initialisation est censée être la valeur exacte de l’object, utilisez l’initialisation copy ( = ) (car en cas d’erreur, vous n’invoquerez jamais accidentellement un constructeur explicite, qui interprète généralement valeur différemment). Dans les endroits où l’initialisation de la copie n’est pas disponible, voyez si l’initialisation des accolades a la sémantique correcte, et si oui, utilisez-la; sinon utilisez l’initialisation des parenthèses (si cela n’est pas disponible, vous n’avez pas de chance).

    • Si les valeurs avec lesquelles vous initialisez sont une liste de valeurs à stocker dans l’object (comme les éléments d’un vecteur / tableau ou d’une partie réelle / imaginaire d’un nombre complexe), utilisez les accolades si elles sont disponibles.

    • Si les valeurs que vous initialisez avec ne sont pas des valeurs à stocker, mais décrivent la valeur / l’état prévu de l’object, utilisez des parenthèses. Les exemples sont l’argument de taille d’un vector ou l’argument de nom de fichier d’un fstream .

    Je suis presque certain qu’il n’y aura jamais de ligne direcsortingce universelle. Mon approche consiste à utiliser des accolades toujours bouclées en se rappelant que

    1. Les constructeurs de listes d’initialisation ont priorité sur les autres constructeurs
    2. Tous les conteneurs de bibliothèque standard et std :: basic_ssortingng ont des constructeurs de listes d’initialisation.
    3. L’initialisation des accolades ne permet pas de réduire les conversions.

    Les accolades rondes et bouclées ne sont donc pas interchangeables. Mais le fait de savoir où ils diffèrent me permet d’utiliser l’initialisation des parenthèses arrondies dans la plupart des cas (certains des cas où je ne suis pas actuellement un bogue du compilateur).

    En dehors du code générique (c.-à-d. Des modèles), vous pouvez (et je le fais) utiliser des accolades partout . L’un des avantages est qu’il fonctionne partout, par exemple pour l’initialisation en classe:

     struct foo { // Ok std::ssortingng a = { "foo" }; // Also ok std::ssortingng b { "bar" }; // Not possible std::ssortingng c("qux"); // For completeness this is possible std::ssortingng d = "baz"; }; 

    ou pour des arguments de fonction:

     void foo(std::pair); foo({ 42, nullptr }); // Not possible with parentheses without spelling out the type: foo(std::pair(42, nullptr)); 

    Pour les variables, je ne fais pas très attention au T t = { init }; ou T t { init }; styles, je trouve que la différence est mineure et au pire ne débouchera que sur un message utile du compilateur concernant l’utilisation abusive d’un constructeur explicit .

    Pour les types qui acceptent std::initializer_list bien que parfois les constructeurs non std::initializer_list sont nécessaires (l’exemple classique étant std::vector twenty_answers(20, 42); ). C’est bien de ne pas utiliser d’accolades alors.


    En ce qui concerne le code générique (c.-à-d. Dans les modèles), ce dernier paragraphe aurait dû déclencher des avertissements. Considérer ce qui suit:

     template std::unique_ptr make_unique(Args&&... args) { return std::unique_ptr { new T { std::forward(args)... } }; } 

    Puis auto p = make_unique>(20, T {}); crée un vecteur de taille 2 si T est par exemple int , ou un vecteur de taille 20 si T est std::ssortingng . Un signe très révélateur qu’il ya quelque chose de très faux ici est qu’il n’ya pas de trait qui puisse vous sauver ici (par exemple avec SFINAE): std::is_constructible est en termes d’initialisation directe, alors que nous utilisons diffère de l’initialisation directe si et seulement si aucun constructeur ne prend std::initializer_list interférant. De même std::is_convertible n’est d’aucune aide.

    J’ai enquêté sur la possibilité de rouler un trait capable de résoudre ce problème, mais je ne suis pas trop optimiste à ce sujet. En tout cas, je ne pense pas que nous en make_unique(foo, bar) beaucoup, je pense que le fait que make_unique(foo, bar) aboutisse à une construction équivalente à T(foo, bar) est très intuitif; make_unique({ foo, bar }) donné que make_unique({ 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 supérieur à C ++ 03 T t = T(); . Sinon, c’est soit la syntaxe d’initialisation directe (c’est-à-dire T t(a0, a1, a2); ), soit parfois la construction par défaut ( T t; stream >> t;

    Cela ne signifie pas que toutes les accolades sont mauvaises, considérez l’exemple précédent avec des correctifs:

     template std::unique_ptr make_unique(Args&&... args) { return std::unique_ptr { new T(std::forward(args)...) }; } 

    Cela utilise encore des accolades pour construire le std::unique_ptr , même si le type réel dépend du paramètre de modèle T