7 votes

Initialisation uniforme C++11 : ambiguïté entre liste d'initialisation et constructeurs à paramètres multiples ?

J'essaie actuellement de me faire une idée de l'initialisation uniforme de C++11. Je suis tombé sur ce cas ambigu : considérons une classe qui peut être construite soit à partir d'un constructeur à deux arguments, soit à partir d'une liste d'initialisation de longueur quelconque :

class Foo {
  public:
    Foo(int a, int b) {
      std::cout << "constructor 1" << std::endl;
    }
    Foo(std::initializer_list<int>) {
      std::cout << "constructor 2" << std::endl;
    }
};

En suivant la convention d'initialisation uniforme, je m'attends à ce que ce qui suit fonctionne :

Foo a (1, 2) imprime constructor 1 (duh)

Foo b {1, 2} imprime constructor 1

Foo c = {1, 2} imprime constructor 2

Cependant, il semble que le compilateur interprète Foo b {1, 2} comme une initialisation de liste, et appelle le constructeur 2. Est-ce que le () syntaxe le seul moyen de forcer le compilateur à prendre en compte d'autres types de constructeurs lorsqu'un constructeur de type liste d'initialisation est présent ?

3 votes

En rapport : stackoverflow.com/questions/9976927/ -- std::vector est un "exemple classique", bien que je n'en trouve que peu de mention sur SO.

1voto

bipll Points 7287

Vous pouvez ajouter un argument ignoré supplémentaire à votre constructeur pour spécifier une surcharge particulière au niveau de l'appel, comme c'est le cas dans la STL :

#include <iostream>

struct non_init_list_t {};
inline constexpr non_init_list_t non_init_list;

struct Class {
    Class(int a, int b, non_init_list_t = non_init_list) { std::clog << "()\n"; }
    Class(std::initializer_list<int> list) { std::clog << "{}\n"; }
};

Class a{12, 42, non_init_list};  // ()
Class b{12, 42};                 // {}
Class c(12, 42);                 // ()

1voto

Andreas DM Points 4427

il semble que le compilateur interprète Foo b {1, 2} comme une liste et appelle le constructeur 2. La syntaxe () est-elle le seul moyen de forcer le compilateur à considérer d'autres types de constructeurs lorsqu'un initialisateur-liste est présent ?

Les citations de l'avant-projet standard l'expliquent bien :

9.4.5.2 [dcl.init.list] (souligné par moi) :

Un constructeur est un constructeur de liste d'initialisation si sa première liste ou référence à cvist pour un certain type E, et s'il n'y a pas d'autres paramètres. d'autres paramètres, soit tous les autres paramètres ont des arguments par défaut ([dcl.fct.default]). std::initializer_l est de type std::initializer_

[Note 2 : Les constructeurs de listes d'initialisation sont favorisé par rapport à d'autres constructeurs dans l'initialisation de liste ([over.match.list]). En passant une liste d'initialisation comme argument à au constructeur template C(T) d'une classe C ne crée pas de pas un constructeur de liste d'initialisation, parce qu'un argument de liste d'initialisation fait que le paramètre correspondant est un contexte non déduit ([temp.deduct]). non déduits ([temp.deduct.call]). - note de fin]

et 12.4.2.8 [over.match.list] :

Lorsque des objets de classe non agrégée de type T sont initialisés à la liste de telle sorte que que [dcl.init.list] spécifie que la résolution des surcharges est effectuée conformément aux règles de cette sous-clause ou lors de la formation d'une séquence d'initialisation de liste conformément à [ ]. séquence d'initialisation de liste selon [over.ics.list], la résolution de surcharge sélectionne le constructeur en deux phases :

  • Si la liste des initialisateurs n'est pas vide ou si T n'a pas de constructeur par défaut, la résolution des surcharges est d'abord effectuée lorsque les fonctions candidates sont les constructeurs de la liste d'initialisation ([dcl.init.list]) de la classe T et la liste des arguments est constituée de la liste des initialisateurs sous la forme d'un seul argument.

  • Dans le cas contraire, ou si aucun constructeur de liste d'initialisation viable n'est trouvé , la résolution de la surcharge est effectuée à nouveau , où le fonctions du candidat sont tous les constructeurs de la classe T et la liste des arguments se compose de des éléments de la liste d'initialisation.

0voto

Steve Wilson Points 104

Si le constructeur possède l'option initializer_list le compilateur l'interprétera d'abord comme une version initializer_list s'il n'y a pas de initializer_list le compilateur l'interprétera comme une autre version surchargée. Si le compilateur l'interprète comme une autre version, et que vous voulez appeler un constructeur qui utilise la version initializer_list il arrive que le nombre et le type d'arguments soient les mêmes que ceux des autres ctors, alors vous provoquerez un bogue. Le compilateur choisit alors l'option initializer_list ou une autre version ? L'utilisation de la notation entre crochets n'est donc certainement pas la initializer_list version. Si vous n'avez pas de initializer_list dans votre constructeur, ne vous préoccupez pas de ce problème.

BTW, si vous utilisez auto pour déduire automatiquement le type, N'UTILISEZ PAS l'initialisation uniforme. Elle doit interpréter le type en initializer_list .

Prograide.com

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.

Powered by:

X