La bibliothèque standard C++ de Nicolai M. Josuttis indique:
Il y a une différence mineure entre
X x;
Y y(x) //explicit conversion
et
X x;
Y y = x; //implicit conversion
À la suite de dire: "Le premier crée un nouvel objet de type Y en utilisant une conversion explicite à partir du type X, tandis que le dernier crée un nouvel objet de type Y en utilisant une conversion implicite."
Je suis un peu confus quant aux concepts de conversion explicite et implicite. Dans les deux cas, vous prenez un X et le poussez dans un Y en tant que tel - on utilise le constructeur du Y et l’opérateur d’affectation.
Quelle est la différence dans la façon dont la conversion est traitée dans ces deux cas, qu'est-ce qui la rend explicite/implicite, et comment est-ce lié à la création d'un constructeur de classe défini avec la clé "explicite", le cas échéant?
on utilise cependant le constructeur de Y et l’opérateur d’affectation.
Nan. Dans le second cas, il ne s'agit pas d'une affectation, mais d'une initialisation , l'opérateur d'affectation (operator=
) n'est jamais appelé. à la place, un constructeur non -explicit
à un paramètre (qui accepte le type X
en tant que paramètre) est appelé.
La différence entre initialisation et affectation est importante: dans le premier cas, un nouvel objet est créé et il commence sa vie avec la valeur avec laquelle il est initialisé (d'où la raison pour laquelle un constructeur est appelé), tandis que l'affectation a lieu lorsqu'un objet est assigné (~ copié) à un objet qui existe déjà et est déjà dans un état défini .
Quoi qu'il en soit, les deux formes d'initialisation que vous avez écrites diffèrent par le fait que dans le premier cas, vous appelez explicitement un constructeur, et donc tout constructeur est acceptable; dans le second cas, vous appelez un constructeur implicitement , puisque vous n'utilisez pas la syntaxe de constructeur "classique", mais la syntaxe d'initialisation.
Dans ce cas, seuls les constructeurs à un paramètre non marqués avec explicit
sont acceptables. De telles personnes sont appelées par certaines personnes "convertissant" des constructeurs, car elles sont impliquées dans des conversions implicites.
Comme spécifié dans cette autre réponse , tout constructeur non marqué explicit
peut prendre part à une conversion implicite, par exemple. convertir un objet passé en fonction au type attendu par cette fonction. En fait, vous pouvez dire que c'est ce qui se passe dans votre deuxième exemple: vous voulez initialiser (= créer avec une valeur copiée ailleurs) y
avec x
, mais x
doit d'abord être converti au type Y
, ce qui est fait avec le constructeur implicite .
Ce type de conversion implicite est souvent souhaitable: pensez par exemple à une classe de chaîne ayant un constructeur de conversion (c'est-à-dire non -explicit
) à partir d'un const char *
: toute fonction recevant un paramètre string
peut également être appelée avec une chaîne de caractères "normale" : en raison du constructeur de conversion, l'appelant utilisera les chaînes de caractères C, l'appelé recevra son objet string
.
Néanmoins, dans certains cas, les constructeurs à un paramètre peuvent ne pas être appropriés pour la conversion: cela se produit généralement lorsque leur seul paramètre n'est pas "converti" conceptuellement en un type d'objet créé, mais qu'il ne s'agit que d'un paramètre pour la construction; pensez par exemple à un objet de flux de fichiers: il aura probablement un constructeur qui accepte le nom du fichier à ouvrir, mais il n’a aucun sens de dire qu’une telle chaîne est "convertie" en un flux fonctionnant sur ce fichier.
Vous pouvez également trouver des scénarios plus complexes dans lesquels ces conversions implicites peuvent complètement gâcher le comportement que le programmeur attend de la résolution de la surcharge; Vous trouverez des exemples de cela dans les réponses ci-dessous à celle que j'ai liée ci-dessus.
Plus simplement, il peut également arriver que certains constructeurs soient très lourds. Le concepteur de classe peut donc vouloir s'assurer qu'ils sont appelés explicitement. Dans ces cas, le constructeur est marqué comme suit: explicit
. Il ne peut donc être utilisé que lorsqu'il est appelé "explicitement en tant que constructeur" et ne participe pas aux conversions implicites.
La première forme est initialisation directe . La seconde est copie de l'initialisation .
L'initialisation de copie appelle implicitement un constructeur de conversion ou un opérateur de conversion, puis explicitement un constructeur de copie (l'appel du constructeur de copie peut être éliminé, mais une vérification de l'accessibilité doit encore être effectuée).
Envisagez une troisième possibilité, l’initialisation par copie, mais la conversion est explicite:
Y y = Y(x);
ou
Y y = (Y)x;
on utilise cependant l'opérateur d'affectation
Non. Il appelle directement le constructeur.
La raison pour laquelle l'un est explicite et l'autre implicite est parce que des conversions implicites peuvent se produire lorsque vous ne le souhaitez pas. Les explicites ne peuvent pas. L'exemple le plus simple est bool.
Disons que vous inventez un type qui peut être vrai ou faux, comme un pointeur. Ensuite, ajoutons que vous décidez que, pour simplifier la vie de vos utilisateurs, vous le laissez convertir implicitement en booléen. C’est formidable, jusqu’au point où l’un de vos utilisateurs fait quelque chose de stupide.
int i = 0;
i = i >> MyUDT();
Oh, attends, pourquoi est-ce même compilé? Vous ne pouvez pas déplacer un MyUDT du tout! Il compile parce que bool
est un type entier. Le compilateur l'a implicitement converti en booléen, puis en quelque chose qui peut être déplacé. Le code ci-dessus est manifestement stupide - nous voulons seulement que les gens puissent se convertir en un bool , pas un bool et tout ce qu'un bool pourrait vouloir faire.
C'est pourquoi les opérateurs de conversion explicites ont été ajoutés à C++ 0x.
Le Casting implicite ne nécessite aucun opérateur de casting. Ce transtypage est normalement utilisé lors de la conversion de données de types intégraux plus petits en types plus grands ou dérivés au type de base.
int iVal = 100; double dVal = iVal;
Le constructeur explicit converting est préféré à l'opérateur de conversion implicite car, dans ce dernier cas, il existe un appel supplémentaire au constructeur de copie. Conversions implicites et explicites