Pourquoi le constructeur n’est pas appelé opérateur de casting donné?

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:

  • sur a : Wrap::operator A*() devrait être appelé
  • sur le résultat: 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:

  • pourrait surprendre les utilisateurs – les conversions implicites sont peut-être déjà surprenantes en l’état …
  • pourrait conduire à une recherche exponentielle des possibilités (pour le compilateur) – il faudrait aller des deux côtés, en essayant de vérifier toutes les conversions possibles, et en quelque sorte “joindre” les deux (avec le chemin le plus court possible).

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