38 votes

Ce qui pourrait aller mal si la copie de la liste d'initialisation autorisent expressément les constructeurs?

Dans la norme C++, §13.3.1.7 [plus.match.liste], comme indiqué:

Dans la copie de la liste d'initialisation, si un explicit constructeur est choisi, l'initialisation est mal formé.

C'est la raison pour laquelle nous ne pouvons pas faire, par exemple, quelque chose comme ceci:

struct foo {
    // explicit because it can be called with one argument
    explicit foo(std::string s, int x = 0);
private:
    // ...
};

void f(foo x);

f({ "answer", 42 });

(Notez que ce qui se passe ici n'est pas une conversion, et il ne serait pas même si le constructeur était "implicite". C'est l'initialisation de foo objet à l'aide de son constructeur directement. Autres que l' std::string, il n'y a pas de conversion ici.)

Cela semble parfaitement bien pour moi. Il n'y a aucun moyen de faire une conversion implicite va me mordre.

Si { "answer", 42 } pouvez initialiser quelque chose d'autre, le compilateur ne pas me trahir et faire la mauvaise chose:

struct bar {
    // explicit because it can be called with one argument
    explicit bar(std::string s, int x = 0);
private:
    // ...
};

void f(foo x);
void f(bar x);

f({ "answer", 42 }); // error: ambiguous call

Il n'y a pas de problème: l'appel est ambigu, le code ne compile pas, et je vais avoir à prendre la surcharge explicitement.

f(bar { "answer", 42 }); // ok

Depuis l'interdiction est explicitement indiqué, j'ai l'impression qu'il me manque quelque chose ici. Aussi loin que je peux voir, initialisation de la liste de la cueillette explicite les constructeurs ne semble pas être un problème pour moi: à l'aide de la liste d'initialisation de la syntaxe, le programmeur est déjà exprimé le désir de faire une sorte de "conversion".

Ce qui pourrait aller mal? Ce qui me manque?

25voto

Johannes Schaub - litb Points 256113

Sur le plan conceptuel copie de la liste d'initialisation est la conversion d'un composé de la valeur à un type de destination. Le papier que le libellé proposé et expliqué justification déjà estimé que le terme "copie" du "copier l'initialisation de la liste" malheureux, car il n'a pas vraiment de transmettre la véritable raison d'être. Mais il est conservé pour la compatibilité avec le libellé existant. Un {10, 20} une paire/n-uplet de la valeur et ne devrait pas être en mesure de copier initialiser un String(int size, int reserve), parce qu'une chaîne n'est pas une paire.

Explicite les constructeurs sont considérés, mais interdit d'être utilisé. Cela a un sens dans les cas comme suit

struct String {
  explicit String(int size);
  String(char const *value);
};

String s = { 0 };

0 ne pas transmettre la valeur d'une chaîne. Donc, cela provoque une erreur, car les deux constructeurs sont pris en considération, mais un explicit constructeur est sélectionné, au lieu de l' 0 d'être traité comme un pointeur null constante.

Malheureusement, cela arrive aussi dans la résolution de surcharge dans l'ensemble de fonctions

void print(String s);
void print(std::vector<int> numbers);

int main() { print({10}); }

C'est mal formé aussi en raison de l'ambiguïté. Certaines personnes (dont moi) avant C++11 a été publié pensé que c'est malheureux, mais n'est pas venu avec un papier de proposer une modification au sujet de cette (autant que je suis conscient).

2voto

Serge Dundich Points 1682

Comme je comprends le but même du mot-clé explicite est de nier implicitement exprimés avec ce constructeur.

Si vous vous demandez pourquoi explicite constructeur ne peut pas être utilisé pour cast implicite? Bien sûr, parce que l'auteur de ce constructeur explicitement nié en utilisant le mot explicite avec elle. La citation de la norme que vous avez posté seulement les etats, qui explicite mot clé s'applique également à l'initialiseur-listes (et pas seulement à de simples valeurs d'un certain type).

AJOUTER:

Pour en dire plus correctement: le but de le mot-clé explicite utilisé avec certains constructeur, il est absolument clair que ce constructeur est utilisé dans un certain endroit (c'est à dire d'imposer à tous le code pour appeler le constructeur de manière explicite).

Et de l'OMI instruction comme celle - f({a,b}) lorsque f est un nom de la fonction n'a rien à voir avec explicite de l'appel du constructeur. C'est absolument pas clair (et dépend du contexte) quel constructeur (et de quel type) est utilisé ici, par exemple, il dépend de la fonction des surcharges présent.

D'autre part quelque chose comme f(SomeType(a,b)) est tout autre chose - il est absolument clair que nous utilisons le constructeur de type SomeType qui prend deux arguments a,b et que nous utilisons la fonction f de surcharge qui sera le meilleur pour accepter seul argument de type SomeType.

Ainsi, certains constructeurs sont OK pour utilisation implicite comme f({a,b}) et d'autres exigent que le fait de leur utilisation est tout à fait évident pour le lecteur, c'est pourquoi nous déclarer explicite.

ADD2:

Mon point est: Parfois il absolument du sens à déclarer les constructeurs explicites, même si rien ne pouvait aller mal. IMO si le constructeur explicite est plus une question de logique que les mises en garde de toute nature.

E. g.

double x = 2; // looks absolutely natural
std::complex<double> x1 = 3;  // also looks absolutely natural
std::complex<double> x2 = { 5, 1 };  // also looks absolutely natural

Mais

std::vector< std::set<std::string> >  seq1 = 7; // looks like nonsense
std::string str = some_allocator; // also looks stupid

2voto

Nicol Bolas Points 133791

Cette instruction:

Dans la copie de la liste d'initialisation, si un explicit constructeur est choisi, l'initialisation est mal formé.

signifie beaucoup de choses. Parmi eux, il signifie qu'il doit chercher à des constructeurs. Après tout, il ne peut pas sélectionner un constructeur explicite s'il ne peut pas le regarder. Quand il recherche des candidats pour convertir les entretoises de la liste, il doit choisir parmi tous les candidats. Même ceux qui plus tard sera jugée illégale.

Si la résolution de surcharge résultats dans de multiples fonctions étant tout aussi viable, alors il en résulte que dans l'ambiguïté d'un appel qui nécessite une intervention manuelle de l'utilisateur.

1voto

Tom Tanner Points 4148

N'est-ce pas parce que 'explicite' est là pour arrêter la conversion implicite, et vous demandons de faire un cast implicite?

Souhaitez-vous poser la question si l'on a spécifié la structure avec un seul argument du constructeur?

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