struct A {}; struct B { B (A* pA) {} B& operator = (A* pA) { return *this; } }; template struct Wrap { T *x; operator T* () { return x; } }; int main () { Wrap a; B oB = a; // error: conversion from 'Wrap' to non-scalar type 'B' requested oB = a; // ok }
Quand oB
est construit, alors pourquoi B::B(A*)
N’EST PAS appelé pour Wrap::operator T ()
? [Remarque: B::operator = (A*)
est appelé pour Wrap::operator T ()
dans l’instruction suivante]
Le problème est que le nombre de conversions définies par l’utilisateur invoquées implicitement est limité (à 1) par la norme.
B ob = a;
implique deux conversions utilisateur:
a
: Wrap::operator A*()
devrait être appelé B::B(A*)
devrait être appelé Explication de @James Kanze: cette syntaxe s’appelle “initialisation de la copie”, ce qui équivaut en réalité à B ob = B(a)
(la copie étant généralement supprimée). Ceci est différent de B ob(a)
qui est une “initialisation directe” et aurait fonctionné.
si vous qualifiez explicitement cela, cela fonctionnera, par exemple:
B ob = B(a);
Par contre, pour le second cas, il n’ya pas de problème:
ob = a;
est un raccourci pour:
ob.operator=(a);
Et par conséquent, une seule conversion définie par l’utilisateur est requirejse, ce qui est autorisé.
EDIT :
Comme cela a été demandé dans un commentaire (à la réponse de Kirill), nous pouvons deviner le motif.
Les conversions en chaîne peuvent être longues, très longues et donc:
De plus, tant qu’il ya plus d’une conversion, vous courez le risque d’avoir des cycles qui devraient être détectés (même si le diagnostic ne serait probablement pas nécessaire et serait soumis à la qualité d’implémentation).
Donc, comme une limite est nécessaire pour éviter des recherches infiniment longues (elle aurait pu être laissée non spécifiée, avec un minimum requirejs), et comme au-delà de 1, nous pourrions avoir de nouvelles émissions (cycles), alors 1 semble une limite aussi bonne que tout .
C’est parce que vous utilisez “l’initialisation de la copie”. Si vous écrivez la déclaration de oB
:
B oB(a);
, ça devrait marcher. La sémantique des deux initialisations est différente. Pour B oB(a)
, le compilateur essaie de trouver un constructeur pouvant être appelé avec les arguments donnés. Dans ce cas, B::B(A*)
peut être appelé, car il existe une conversion implicite de Wrap
en A*
. Pour B oB = a
, la sémantique consiste à convertir implicitement a
en type B
, puis à utiliser le constructeur de copie de B
pour initialiser oB
. (La copie réelle peut être optimisée, mais la légalité du programme est déterminée comme si ce n’était pas le cas.) Et il n’y a pas de conversion implicite de Wrap
en B
, uniquement de Wrap
en A*
.
L’affectation fonctionne, bien sûr, car l’opérateur d’affectation prend également un A*
, de sorte que la conversion implicite entre en jeu.
La norme n’autorise pas la conversion implicite chaînée. Si cela était autorisé, vous auriez pu écrire un tel code:
struct A { A(int i) //enable implicit conversion from int to A }; struct B { B(const A & a); //enable implicit conversion from A to B }; struct C { C(const B & b); //enable implicit conversion from B to C }; C c = 10; //error
Vous ne pouvez pas vous attendre à ce que 10
soit converti en A
puis converti en B
puis converti en C
B b = 10; //error for same reason! A a = 10; //okay, no chained implicit conversion! B ba = A(10); //okay, no chained implicit conversion! C cb = B(A(10)); //okay, no chained implicit conversion! C ca = A(10); //error, requires chained implicit conversion
La même règle s’applique à la conversion implicite qui appelle l’ operator T()
implicitement.
Considère ceci,
struct B {}; struct A { A(int i); //enable implicit conversion from int to A operator B(); //enable implicit conversion from B to A }; struct C { C(const B & b); //enable implicit conversion from B to C }; C c = 10; //error
Vous ne pouvez pas vous attendre à ce que 10
soit converti en A
puis converti en B
(à l’aide de l’ operator B()
), puis converti en C
S
De telles conversions implicites chaînées ne sont pas autorisées. Vous devez faire ceci:
C cb = B(A(10); //okay. no chained implicit conversion! C ca = A(10); //error, requires chained implicit conversion
En effet, C ++ Standard n’autorise qu’une seule conversion définie par l’utilisateur. Selon § 12.3 / 4:
Au plus une conversion définie par l’utilisateur (constructeur ou fonction de conversion) est implicitement appliquée à une valeur unique.
B oB = a; // not sortinged: ob( a.operator T*() ), 1 conversion func+1 constructor oB = a; // OK: oB.operator=( a.operator T*() ), 1 conversion func+1 operator=
Pour résoudre ce problème, vous pouvez utiliser une forme explicite d’appel du constructeur:
B oB( a ); // this requires only one implicit user-defined conversion