Como os operadores de conversão funcionam em C ++?

Considere este exemplo simples:

template  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 p; p.think(); // why does not the compiler try substituting Type&? } 

Como os operadores de conversão funcionam em C ++? (ie) quando o compilador tenta replace o tipo definido após o operador de conversão?

Algumas situações aleatórias em que as funções de conversão são usadas e não usadas seguem.

Primeiro, observe que as funções de conversão nunca são usadas para converter no mesmo tipo de class ou em um tipo de class base.

Conversão durante a passagem de argumento

A conversão durante a passagem de argumentos usará as regras para boot de cópia. Essas regras consideram apenas qualquer function de conversão, desconsiderando a conversão para uma referência ou não.

 struct B { }; struct A { operator B() { return B(); } }; void f(B); int main() { f(A()); } // called! 

A passagem de argumentos é apenas um contexto de boot de cópia. Outra é a forma “pura” usando a syntax de boot de cópia

 B b = A(); // called! 

Conversão para referência

No operador condicional, a conversão para um tipo de referência é possível, se o tipo convertido em for um lvalue.

 struct B { }; struct A { operator B&() { static B b; return b; } }; int main() { B b; 0 ? b : A(); } // called! 

Outra conversão para referência é quando você vincula uma referência, diretamente

 struct B { }; struct A { operator B&() { static B b; return b; } }; B &b = A(); // called! 

Conversão para pointers de function

Você pode ter uma function de conversão para um ponteiro de function ou referência e, quando uma chamada é feita, ela pode ser usada.

 typedef void (*fPtr)(int); void foo(int a); struct test { operator fPtr() { return foo; } }; int main() { test t; t(10); // called! } 

Essa coisa pode se tornar bastante útil às vezes.

Conversão para tipos não de class

As conversões implícitas que ocorrem sempre e em toda parte também podem usar conversões definidas pelo usuário. Você pode definir uma function de conversão que retorna um valor booleano

 struct test { operator bool() { return true; } }; int main() { test t; if(t) { ... } } 

(A conversão para bool nesse caso pode ser tornada mais segura pelo idioma safe-bool , para proibir conversões para outros tipos inteiros.) As conversões são acionadas em qualquer lugar onde um operador interno espera um determinado tipo. As conversões podem entrar no caminho, no entanto.

 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 

A chamada pode ser ambígua, porque para o membro, o segundo parâmetro precisa de uma conversão e, para o operador interno, o primeiro precisa de uma conversão definida pelo usuário. Os outros dois parâmetros combinam perfeitamente, respectivamente. A chamada pode ser não ambígua em alguns casos ( ptrdiff_t precisa ser diferente de int então).

Modelo de function de conversão

Modelos permitem algumas coisas boas, mas é melhor ter muito cuidado com elas. O seguinte faz um tipo conversível para qualquer tipo de ponteiro (pointers de membro não são vistos como “tipos de ponteiro”).

 struct test { template operator T*() { return 0; } }; void *pv = test(); bool *pb = test(); 

O “.” operador não é sobrecarregável em C ++. E sempre que você disser xy, nenhuma conversão será executada automaticamente em x.

Conversões não são mágicas. Só porque A tem uma conversão para B e B tem um método foo não significa que a.foo () irá chamar B :: foo ().

O compilador tenta usar uma conversão em quatro situações

  1. Você explicitamente converter uma variável para outro tipo
  2. Você passa a variável como um argumento para uma function que espera um tipo diferente nessa posição (os operadores contam como funções aqui)
  3. Você atribui a variável a uma variável de um tipo diferente
  4. Você usa a variável copy-construct ou inicializa uma variável de um tipo diferente

Existem três tipos de conversões, além daquelas envolvidas com inheritance

  1. Conversões internas (por exemplo, int-para-duplo)
  2. Construção implícita, em que a class B define um construtor usando um único argumento do tipo A e não o marca com a palavra-chave “explícita”
  3. Operadores de conversão definidos pelo usuário, onde a class A define um operador B (como no seu exemplo)

Como o compilador decide qual tipo de conversão usar e quando (especialmente quando há várias opções) é bastante complicado, e eu faria um trabalho ruim ao tentar condensá-lo em uma resposta sobre SO. A seção 12.3 do padrão C ++ discute a construção implícita e os operadores de conversão definidos pelo usuário.

(Pode haver algumas situações ou methods de conversão que eu não tenha pensado, então, por favor, comente ou edite-os se vir algo faltando)

A conversão implícita (seja por operadores de conversão ou construtores não explícitos) ocorre ao passar parâmetros para funções (incluindo operadores sobrecarregados e padrão para classs). Além disso, existem algumas conversões implícitas realizadas em tipos aritméticos (portanto, adicionar um caractere e resultados longos na adição de dois longos, com um resultado longo).

A conversão implícita não se aplica ao object no qual uma chamada de function de membro é feita: para fins de conversão implícita, “this” não é um parâmetro de function.

Você deveria fazer

 ((person)p).think(); 

O compilador não tem as informações para transmitir automaticamente para pessoa, então você precisa de conversão explícita.

Se você usasse algo como

 person pers = p; 

Em seguida, o compilador tem informações para conversão implícita para pessoa.

Você pode ter “casting” por meio de construtores:

 class A { public: A( int ); }; A a = 10; // Looks like a cast from int to A 

Estes são alguns exemplos breves. Casting (implícito, explícito, etc) precisa de mais para explicar. Você pode encontrar detalhes em livros C ++ sérios (veja as perguntas sobre livros C ++ sobre estouro de pilha para bons títulos, como este ).

O compilador tentará uma (!) Conversão definida pelo usuário (operador implícito ou cast) se você tentar usar um object (referência) do tipo T onde U é necessário.

O. operador, no entanto, sempre tentará acessar um membro do object (referência) em seu lado esquerdo. É assim que é definido. Se você quer algo mais chique, é para isso que o operator->() pode ser sobrecarregado.

// Virtual table Fuction (VFT)

 #include  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; }