Considérons cet exemple simple:
template <class Type>
class smartref {
public:
smartref() : data(new Type) { }
operator Type&(){ return *data; }
private:
Type* data;
};
class person {
public:
void think() { std::cout << "I am thinking"; }
};
int main() {
smartref<person> p;
p.think(); // why does not the compiler try substituting Type&?
}
Comment fonctionnent les opérateurs de conversion en C++? (i.e) quand le compilateur essaie-t-il de substituer le type défini après l'opérateur de conversion?
Certaines situations aléatoires où des fonctions de conversion sont utilisées et non utilisées suivent.
Tout d'abord, notez que les fonctions de conversion ne sont jamais utilisées pour convertir le même type de classe ou un type de classe de base.
La conversion lors du passage d'argument utilisera les règles d'initialisation de la copie. Ces règles ne prennent en compte que les fonctions de conversion, qu’elles soient converties en référence ou non.
struct B { };
struct A {
operator B() { return B(); }
};
void f(B);
int main() { f(A()); } // called!
La transmission d'arguments n'est qu'un des contextes d'initialisation de la copie. Une autre est la forme "pure" utilisant la syntaxe d'initialisation de copie
B b = A(); // called!
Dans l'opérateur conditionnel, la conversion en un type de référence est possible, si le type converti en est une lvalue.
struct B { };
struct A {
operator B&() { static B b; return b; }
};
int main() { B b; 0 ? b : A(); } // called!
Une autre conversion en référence est lorsque vous liez une référence, directement
struct B { };
struct A {
operator B&() { static B b; return b; }
};
B &b = A(); // called!
Vous pouvez avoir une fonction de conversion en un pointeur ou une référence de fonction et, lorsqu'un appel est passé, elle peut être utilisée.
typedef void (*fPtr)(int);
void foo(int a);
struct test {
operator fPtr() { return foo; }
};
int main() {
test t; t(10); // called!
}
Cette chose peut devenir utile parfois.
Les conversions implicites qui se produisent toujours et partout peuvent également utiliser des conversions définies par l'utilisateur. Vous pouvez définir une fonction de conversion qui renvoie une valeur booléenne
struct test {
operator bool() { return true; }
};
int main() {
test t;
if(t) { ... }
}
(Dans ce cas, la conversion en bool peut être sécurisée par le safe-bool idiom , pour interdire les conversions vers d'autres types entiers.) Les conversions sont déclenchées partout où un opérateur intégré attend un certain type. Les conversions peuvent toutefois être gênantes.
struct test {
void operator[](unsigned int) { }
operator char *() { static char c; return &c; }
};
int main() {
test t; t[0]; // ambiguous
}
// (t).operator[] (unsigned int) : member
// operator[](T *, std::ptrdiff_t) : built-in
L'appel peut être ambigu, car pour le membre, le second paramètre nécessite une conversion et pour l'opérateur intégré, le premier nécessite une conversion définie par l'utilisateur. Les deux autres paramètres correspondent parfaitement, respectivement. L'appel peut être non ambigu dans certains cas (ptrdiff_t
doit alors être différent de int
).
Les modèles permettent quelques jolies choses, mais il vaut mieux être très prudent à leur sujet. Ce qui suit rend un type convertible en n'importe quel type de pointeur (les pointeurs de membres ne sont pas vus comme des "types de pointeurs").
struct test {
template<typename T>
operator T*() { return 0; }
};
void *pv = test();
bool *pb = test();
Le "." L'opérateur n'est pas surchargeable en C++. Et chaque fois que vous dites x.y, aucune conversion ne sera automatiquement effectuée sur x.
Les conversions ne sont pas magiques. Ce n'est pas parce que A a une conversion en B et B a une méthode foo que a.foo () appellera B :: foo ().
Le compilateur essaie d'utiliser une conversion dans quatre situations
Il existe trois types de conversions, autres que ceux liés à l'héritage
Comment le compilateur décide-t-il quel type de conversion utiliser et quand (surtout lorsqu'il y a plusieurs choix) est assez complexe, et je ferais un mauvais travail en essayant de le condenser en une réponse à SO. La section 12.3 de le standard C++ traite de la construction implicite et des opérateurs de conversion définis par l'utilisateur.
(Il y a peut-être des situations ou des méthodes de conversion auxquelles je n'ai pas pensé, alors merci de les commenter ou de les éditer si quelque chose manque.)
La conversion implicite (que ce soit par des opérateurs de conversion ou par des constructeurs non explicites) se produit lors de la transmission de paramètres à des fonctions (y compris les opérateurs surchargés et par défaut pour les classes). En plus de cela, certaines conversions implicites sont effectuées sur des types arithmétiques (ainsi, l'ajout d'un caractère et d'un long entraîne l'ajout de deux longs, avec un résultat long).
La conversion implicite ne s'applique pas à l'objet sur lequel un appel de fonction membre est effectué: aux fins de la conversion implicite, "this" n'est pas un paramètre de fonction.
Tu devrais faire
((person)p).think();
Le compilateur ne dispose pas des informations nécessaires à la conversion automatique en personne, vous avez donc besoin d'une diffusion explicite.
Si vous voulez utiliser quelque chose comme
person pers = p;
Ensuite, le compilateur a des informations pour la conversion implicite à personne.
Vous pouvez avoir "casting" à travers les constructeurs:
class A
{
public:
A( int );
};
A a = 10; // Looks like a cast from int to A
Voici quelques exemples brefs. Le casting (implicite, explicite, etc.) nécessite plus d'explications. Vous pouvez trouver des détails dans des livres C++ sérieux (voir les questions sur les livres C++ sur le dépassement de capacité de la pile pour obtenir de bons titres, comme celui-ci ).
// Table virtuelle Fuction (VFT)
#include <iostream>
using namespace std;
class smartref {
public:
virtual char think() { }//for Late bindig make virtual function if not make virtual function of char think() {} then become early binding and pointer call this class function
smartref() : data(new char) { }
operator char(){ return *data; }
private:
char* data;
};
class person:public smartref
{
public:
char think() { std::cout << "I am thinking"; }
};
int main() {
smartref *p;//make pointer of class
person o1;//make object of class
p=&o1;//store object address in pointer
p->think(); // Late Binding in class person
return 0;
}
Le compilateur tentera une conversion (!) Définie par l'utilisateur (opérateur implicite ou opérateur) si vous essayez d'utiliser un objet (référence) de type T
où U
est requis.
Cependant, l'opérateur .
essaiera toujours d'accéder à un membre de l'objet (référence) sur son côté gauche. C'est juste la façon dont c'est défini. Si vous voulez quelque chose de plus sophistiqué, c'est pour cela que operator->()
peut être surchargé.