Conversões padrão

A linguagem C++ define conversões entre seus tipos básicos. Ela também define conversões para o ponteiro, referência e tipos derivados de ponteiro ao membro. Essas conversões são chamadas de conversões padrão.

Esta seção aborda as seguintes conversões padrão:

  • Promoções de integral

  • Conversões de integral

  • Conversões flutuantes

  • Conversões flutuantes e integrais

  • Conversões aritméticas

  • Conversões de ponteiro

  • Conversões de referência

  • Conversões de ponteiro ao membro

    Observação

    Os tipos definidos pelo usuário podem especificar suas próprias conversões. A conversão de tipos definidos pelo usuário está coberta em Construtores e em Construtores e Conversões.

O código a seguir causa conversões (neste exemplo, promoções de integral):

long  long_num1, long_num2;
int   int_num;

// int_num promoted to type long prior to assignment.
long_num1 = int_num;

// int_num promoted to type long prior to multiplication.
long_num2 = int_num * long_num2;

O resultado de uma conversão é um l-value somente se ela produzir um tipo de referência. Por exemplo, uma conversão definida pelo usuário declarada como operator int&() retorna uma referência e é um valor l. No entanto, uma conversão declarada com operator int() retorna um objeto e não é um valor l.

Promoções de integral

Objetos de um tipo integral podem ser convertidos em outro tipo integral mais amplo, ou seja, um tipo que pode representar um conjunto maior de valores. Esse tipo de conversão em expansão é chamado de promoção integral. Com a promoção integral, você poderá usar os seguintes tipos em uma expressão sempre que outro tipo integral puder ser usado:

  • Objetos, literais e constantes do tipo char e short int

  • Tipos de enumeração

  • campos de bits int

  • Enumeradores

As promoções de C++ são "preservadoras de valor", pois o valor após a promoção tem a garantia de ser o mesmo que o valor anterior da promoção. Nas promoções de preservação de valores, os objetos de tipos integrais mais curtos (como campos de bits ou objetos do tipo char) serão promovidos para o tipo int se int puder representar a gama completa do tipo original. Se int não puder representar o intervalo completo de valores, o objeto será promovido para o tipo unsigned int. Embora essa estratégia seja a mesma usada pelo Padrão C, as conversões de preservação de valor não preservam a "assinatura" do objeto.

As promoções de preservação de valores e as promoções que preservam o signedness normalmente geram os mesmos resultados. No entanto, poderão produzir resultados diferentes se o objeto promovido aparecer como:

  • Um operando de/, %, /=, %=, <, <=, > ou >=

    Esses operadores dependem do sinal para determinar o resultado. As promoções de preservação de valor e de preservação de sinal produzem resultados diferentes quando aplicadas a esses operandos.

  • O operando esquerdo de >> ou >>=

    Esses operadores tratam quantidades com sinal e sem sinal de maneira diferente em uma operação de turno. Para quantidades com sinal, uma operação de deslocamento para a direita propaga o bit de sinal nas posições de bit desocupadas, enquanto as posições de bit desocupadas são preenchidas com zero em quantidades sem sinal.

  • Um argumento para uma função sobrecarregada, ou o operando de um operador sobrecarregado, que depende da assinatura do tipo de operando para correspondência de argumentos. Para obter mais informações sobre como definir operadores sobrecarregados, consulte Operadores sobrecarregados.

Conversões de integral

Conversões integrais são conversões entre tipos integrais. Os tipos integrais são char, short (ou short int), int, longe long long. Esses tipos podem ser qualificados com ou signed ou unsigned e unsigned podem ser usados como abreviação de unsigned int.

Assinado para sem sinal

Os objetos de tipos integrais com sinal podem ser convertidos nos tipos sem sinal correspondentes. Quando essas conversões ocorrem, o padrão de bits real não altera. No entanto, a interpretação dos dados altera. Considere este código:

#include <iostream>

using namespace std;
int main()
{
    short  i = -3;
    unsigned short u;

    cout << (u = i) << "\n";
}
// Output: 65533

No exemplo acima, um signed short, i, é definido e inicializado como um número negativo. A expressão (u = i) faz com que i seja convertido em um unsigned short antes da atribuição a u.

Sem sinal para com sinal

Os objetos de tipos integrais sem sinal podem ser convertidos nos tipos com sinal correspondentes. No entanto, se o valor sem sinal estiver fora do intervalo representável do tipo com sinal, o resultado não terá o valor correto, conforme demonstrado no exemplo a seguir:

#include <iostream>

using namespace std;
int main()
{
short  i;
unsigned short u = 65533;

cout << (i = u) << "\n";
}
//Output: -3

No exemplo acima, u é um objeto integral unsigned short que deve ser convertido em uma quantidade com sinal para avaliar a expressão (i = u). Como o valor não pode ser representado adequadamente em um signed short, os dados são interpretados incorretamente conforme mostrado.

Conversões de ponto flutuante

Um objeto de um tipo flutuante pode ser convertido com segurança em um tipo flutuante mais preciso, ou seja, a conversão não causa perda de significância. Por exemplo, as conversões de float para double ou de double para long double são seguras e o valor não é alterado.

Um objeto de tipo flutuante também poderá ser convertido em um tipo menos preciso, se estiver em um intervalo representável por esse tipo. (Consulte Limites Flutuantes para os intervalos de tipos flutuante.) Se o valor original não for representável com precisão, ele poderá ser convertido no valor representável imediatamente superior ou inferior. O resultado será indefinido se esse valor não existir. Considere o seguinte exemplo:

cout << (float)1E300 << endl;

O valor máximo representável por tipo float é 3.402823466E38, que é um número muito menor do que 1E300. Portanto, o número é convertido para infinito e o resultado é "inf".

Conversões entre tipos de ponto flutuante e integral

Algumas expressões podem fazer com que os objetos de tipo flutuante sejam convertidos em tipos integrais, ou vice-versa. Quando um objeto do tipo integral é convertido em um tipo flutuante e o valor original não é representável exatamente, o resultado é o valor representável imediatamente superior ou inferior.

Quando um objeto do tipo flutuante é convertido em um tipo integral, a parte fracionária é truncada ou arredondada para zero. Um número como 1,3 é convertido em 1 e -1,3 é convertido em -1. Se o valor truncado for maior que o valor representável mais alto ou menor que o valor representável mais baixo, o resultado será indefinido.

Conversões aritméticas

Muitos operadores binários (abordados em Expressões com operadores binários) causam conversões de operandos e produzem resultados da mesma maneira. As conversões causadas por esses operadores são chamadas de conversões aritméticas usuais. As conversões aritméticas de operandos que possuem diferentes tipos nativos são feitas conforme mostrado na tabela a seguir. Os tipos Typedef se comportam de acordo com seus tipos nativos subjacentes.

Condições para conversão de tipo

Condições atendidas Conversão
Qualquer um dos operandos é do tipo long double. Outro operando é convertido para o tipo long double.
Condição anterior não atendida e qualquer um dos operandos é do tipo double. Outro operando é convertido para o tipo double.
Condições precedentes não atendidas e qualquer um dos operandos é do tipo float. Outro operando é convertido para o tipo float.
As condições anteriores não foram atendidas (nenhum dos operandos é de tipo flutuante). Operandos obtêm promoções integrais da seguinte forma:

− Se qualquer um dos operandos for do tipo unsigned long, o outro operando será convertido para o tipo unsigned long.
− Se a condição anterior não for atendida e se qualquer um dos operandos for do tipo long e o outro do tipo unsigned int, ambos os operandos serão convertidos para o tipo unsigned long.
− Se as duas condições anteriores não forem atendidas e se qualquer um dos operandos for do tipo long, o outro operando será convertido para o tipo long.
− Se as três condições anteriores não forem atendidas e se um dos operandos for do tipo unsigned int, o outro operando será convertido para o tipo unsigned int.
− Se nenhuma das condições anteriores for atendida, ambos os operandos serão convertidos para o tipo int.

O código a seguir ilustra as regras de conversão descritas na tabela:

double dVal;
float fVal;
int iVal;
unsigned long ulVal;

int main() {
   // iVal converted to unsigned long
   // result of multiplication converted to double
   dVal = iVal * ulVal;

   // ulVal converted to float
   // result of addition converted to double
   dVal = ulVal + fVal;
}

A primeira instrução no exemplo acima mostra a multiplicação de dois tipos integrais, iVal e ulVal. A condição atendida é que nenhum dos operandos seja do tipo flutuante e um operando seja do tipo unsigned int. Assim, o outro operando, iVal, é convertido para o tipo unsigned int. Em seguida, o resultado é atribuído a dVal. A condição atendida aqui é que um operando seja do tipo double, então, o resultado unsigned int da multiplicação é convertido para o tipo double.

A segunda instrução no exemplo anterior mostra a adição de um float e um tipo integral: fVal e ulVal. A variável ulVal é convertida para o tipo float (terceira condição da tabela). O resultado da adição é convertido para o tipo double (segunda condição da tabela) e atribuído a dVal.

Conversões de ponteiro

Os ponteiros podem ser convertidos durante a atribuição, a inicialização, a comparação e outras expressões.

Ponteiro para classes

Há dois casos nos quais um ponteiro para uma classe pode ser convertido em um ponteiro para uma classe base.

O primeiros é quando a classe base especificada estiver acessível e a conversão for inequívoca. Para obter mais informações sobre referências ambíguas de classe base, consulte Várias classes base.

O acesso a uma classe base depende do tipo de herança usado na derivação. Considere a herança ilustrada na figura a seguir:

Diagram showing an inheritance graph and base class accessibility.

O diagrama mostra a classe base A. A classe B herda de A por meio de público privado protegido. A classe C herda de B via pública B.

Gráfico de herança ilustrando a acessibilidade de classe base

A tabela a seguir mostra a acessibilidade da classe base para a situação ilustrada na figura.

Tipo de função Derivação Conversão de

B* para A* legal?
Função externa (fora do escopo da classe) Privados Não
Protegido Não
Setor Público Sim
Função membro B (no escopo de B) Privado Sim
Protegido Sim
Público Sim
Função membro C (no escopo de C) Privados Não
Protegido Sim
Público Sim

O segundo caso em que um ponteiro para uma classe pode ser convertido em um ponteiro para uma classe base é quando uma conversão de tipo explícita. Para obter mais informações sobre conversões de tipo explícito, consulte Operador de conversão de tipo explícito.

O resultado dessa conversão é um ponteiro para o subobject, a parte do objeto que é completamente descrita pela classe base.

O código a seguir define duas classes, A e B, onde B é derivado de A. (Para obter mais informações sobre herança, consulte Classes derivadas.) Em seguida, define bObject, um objeto do tipo B, e dois ponteiros (pA e pB) que apontam para o objeto.

// C2039 expected
class A
{
public:
    int AComponent;
    int AMemberFunc();
};

class B : public A
{
public:
    int BComponent;
    int BMemberFunc();
};
int main()
{
   B bObject;
   A *pA = &bObject;
   B *pB = &bObject;

   pA->AMemberFunc();   // OK in class A
   pB->AMemberFunc();   // OK: inherited from class A
   pA->BMemberFunc();   // Error: not in class A
}

O ponteiro pA é do tipo A *, que pode ser interpretado como "ponteiro para um objeto do tipo A". Os membros de bObject (como BComponent e BMemberFunc) são exclusivos do tipo B e, portanto, são inacessíveis por meio de pA. O ponteiro pA permite acesso somente às características (funções membro e dados) do objeto que são definidas na classe A.

Ponteiro para função

Um ponteiro para uma função poderá ser convertido para o tipo void *, se o tipo void * for grande o suficiente para conter esse ponteiro.

Ponteiro para nulo

Ponteiros para o tipo void podem ser convertidos em ponteiros para qualquer outro tipo, mas apenas com um tipo explícito (diferente de C). Um ponteiro para qualquer tipo pode ser convertido implicitamente em um ponteiro para o tipo void. Um ponteiro para um objeto incompleto de um tipo pode ser convertido em um ponteiro para void (implicitamente) e vice-versa (explicitamente). O resultado dessa conversão é igual ao valor do ponteiro original. Um objeto é considerado incompleto se ele é declarado mas não há informações suficientes disponíveis para determinar o seu tamanho ou a sua classe base.

Um ponteiro para qualquer objeto que não const seja ou volatile possa ser convertido implicitamente em um ponteiro do tipo void *.

Ponteiros const e volatile

O C++ não fornece uma conversão padrão de um tipo const ou volatile para um tipo que não seja const ou volatile. No entanto, qualquer tipo de conversão pode ser especificado usando as conversões de tipos explícitas (inclusive as conversões que não são seguras).

Observação

Ponteiros C++ para membros, exceto ponteiros para membros estáticos, são diferentes de ponteiros normais e não têm as mesmas conversões padrão. Os ponteiros para os membros estáticos são ponteiros normais e têm as mesmas conversões que ponteiros normais.

conversões de ponteiro nulo

Uma expressão constante integral avaliada como zero, ou uma expressão convertida em um tipo de ponteiro, é convertida em um ponteiro chamado de ponteiro nulo. Esse ponteiro sempre compara desigual a um ponteiro para qualquer função ou objeto válido. Uma exceção são ponteiros para objetos baseados, que podem ter o mesmo deslocamento e apontar para objetos diferentes.

No C++11, o tipo nullptr deverá ser preferencial para o ponteiro nulo no estilo C.

Conversões de expressão de ponteiro

Qualquer expressão com um tipo de matriz pode ser convertida em um ponteiro do mesmo tipo. O resultado da conversão é um ponteiro para o primeiro elemento da matriz. O exemplo a seguir demonstra essa conversão:

char szPath[_MAX_PATH]; // Array of type char.
char *pszPath = szPath; // Equals &szPath[0].

Uma expressão que resulta em uma função que retorna um tipo específico é convertido em um ponteiro para uma função que retorna esse tipo, exceto quando:

  • A expressão é usada como um operando para o operador de endereço (&).

  • A expressão é usada como um operando para o operador function-call.

Conversões de referência

Uma referência a uma classe poderá ser convertida em uma referência a uma classe base nestes casos:

  • A classe base especificada é acessível.

  • A conversão é inequívoca. (Para obter mais informações sobre referências ambíguas de classe base, consulte Várias classes base.)

O resultado da conversão é um ponteiro para o subobjeto que representa a classe base.

Ponteiro para membro

Os ponteiros para membros de classe podem ser convertidos durante a atribuição, a inicialização, a comparação e outras expressões. Esta seção descreve as seguintes conversões de ponteiros para membros:

Ponteiro para membro da classe base

Um ponteiro para um membro de uma classe base pode ser convertido em um ponteiro para um membro de uma classe derivada dela, quando as seguintes condições são atendidas:

  • A conversão inversa, de ponteiro para classe derivada em ponteiro para classe base, é acessível.

  • A classe derivada não herda virtualmente da classe base.

Quando o operando esquerdo é um ponteiro para um membro, o operando direito deve ser do tipo ponteiro para membro ou ser uma expressão de constante que é avaliada como 0. Essa atribuição só é válida nos seguintes casos:

  • O operando direito é um ponteiro para um membro da mesma classe que o operando esquerdo.

  • O operando esquerdo é um ponteiro para um membro de uma classe derivada, de forma pública e sem ambiguidade, da classe do operando direito.

ponteiro nulo para conversões de membros

Uma expressão constante integral que é avaliada como zero é convertida em um ponteiro nulo. Esse ponteiro sempre compara desigual a um ponteiro para qualquer função ou objeto válido. Uma exceção são ponteiros para objetos baseados, que podem ter o mesmo deslocamento e apontar para objetos diferentes.

O código a seguir ilustra a definição de um ponteiro para o membro i na classe A. O ponteiro, pai, é inicializado como 0, que é o ponteiro nulo.

class A
{
public:
int i;
};

int A::*pai = 0;

int main()
{
}

Confira também

Referência da linguagem C++