Espacios de nombres
Variantes
Acciones

Conversiones implícitas

De cppreference.com
< cpp‎ | language
 
 
 
 

Las conversiones implícitas se realizan cada vez que una expresión de algún tipo T1 se utiliza en un contexto que no acepta ese tipo, pero acepta algún otro tipo T2; en particular:

  • cuando la expresión se utiliza como el argumento al llamar a una función que se declara con T2 como parámetro;
  • cuando la expresión se utiliza como un operando con un operador que espera T2;
  • cuando se inicializa un nuevo objeto de tipo T2, incluyendo la instrucción return en una función que devuelve T2;
  • cuando se usa la expresión en una instrucción switch (T2 es de tipo entero);
  • cuando se usa la expresión en una instrucción if o un bucle (T2 es de tipo bool).

El programa está bien formado (se compila) sólo si existe una secuencia de conversión implícita inequívoca de T1 a T2.

Si hay varias sobrecargas de la función o el operador que se llama, después que la secuencia de conversión implícita se construye a partir de T1 para cada T2 disponible, las reglas de resolución de sobrecarga deciden cuál sobrecarga se compila.

Nota: En expresiones aritméticas, el tipo de destino de las conversiones implícitas en los operandos dados a los operadores binarios se determina por un conjunto de reglas separado: conversiones aritméticas habituales.

Contenido

[editar] Orden de las conversiones

La secuencia de la conversión implícita consiste en lo siguiente, en este orden:

1) cero o una secuencia de conversión estándar;
2) cero o una conversión definida por el usuario;
3) cero o una secuencia de conversión estándar (solo si se usa una conversión definida por el usuario).

Al considerar el argumento de un constructor o de una función de conversión definida por el usuario, se permite solamente una secuencia de conversión estándar (de lo contrario, las conversiones definidas por el usuario podrían estar efectivamente encadenadas). Al convertir de un tipo no-clase a otro tipo no-clase, se permite solamente una secuencia de conversión estándar.

Una secuencia de conversión estándar consiste en lo siguiente, en este orden:

1) cero o una conversión del siguiente conjunto:
  • conversión l-valor a r-valor,
  • conversión de array a puntero, y
  • conversión puntero a función;
2) cero o una promoción numérica o conversión numérica;
3) cero o una conversión puntero a función;
(desde C++17)
4) cero o una conversión de calificación.

Una conversión definida por el usuario consiste de cero o un constructor de conversión no explícito, de un solo argumento, o de una llamada a función de conversión no explícita.

Se dice que una expresión e es implícitamente convertible a T2 si y sólo si la declaración T2 puede ser inicializada por copia de e, es decir, la declaración T2 t = e; está bien formada (puede compilarse) para algún objeto temporal arbitrario t. Observa que esto es diferente de la inicialización directa (T2 t(e)), donde se considerarían adicionalmente los constructores explícitos y las funciones de conversión.

[editar] Conversiones contextuales

En los siguientes contextos, se espera el tipo bool t la conversión implícita se realiza si la declaración bool t(e); está bien formada (es decir, una función de conversión explícita tal como explicit T::operator bool() const; se considera). Tal expresión e se dice que es contextualmente convertida a bool:

  • la expresión de control de if, while, for;
  • los operandos de los operadores lógicos integrados !, && y ||;
  • el primer operando del operador condicional ?:;
  • el predicado en una declaración de aserción estática, static_assert;
  • la expresión en un especificador noexcept;
  • la expresión en un especificador explicit.
(desde C++20)
(desde C++11)

En los siguientes contextos, se espera un tipo T específico del contexto, y la expresión e de tipo clase E solamente se permite si

(hasta C++14)
  • hay exactamente un tipo T entre los tipos permisibles tal que E tiene funciones de conversión no explícitas cuyos tipos de retorno son T (posiblemente calificado-cv) o una referencia a (posiblemente calificado-cv) T , y
  • e es implícitamente convertible a T.
(desde C++14)

Tal expresión e se dice que es contextualmente implícitamente convertida al tipo especificado T. Observa que las funciones de conversión explícitas no se consideran, aun cuando se consideran en las conversiones contextuales a bool. (desde C++11)

  • el argumento de la expresión delete (T es cualquier objeto de tipo puntero);
  • expresión constante entera, donde se usa una clase literal (T es cualquier tipo entero o tipo enumeración sin ámbito, la función de conversión definida por el usuario seleccionada debe ser constexpr);
  • la expresión de control de la instrucción switch (T es cualquier tipo entero o tipo enumeración).
#include <cassert>
 
template<typename T>
class inic_cero
{
    T val;
public:
    inic_cero() : val(static_cast<T>(0)) { }
    inic_cero(T val) : val(val) { }
    operator T&() { return val; }
    operator T() const { return val; }
};
 
int main()
{
    inic_cero<int> i;
    assert(i == 0);
 
    i = 7;
    assert(i == 7);
 
    switch(i) { }     // ERROR hasta C++14 (más de una función de conversión)
                      // de acuerdo desde C++14 (ambas funciones convierten al mismo tipo int)
    switch(i + 0) { } // siempre de acuerdo (conversión implícita)
}

[editar] Transformaciones de valor

Las transformaciones de valor son conversiones que cambian la categoría de valor de una expresión. Toman lugar cada vez que una expresión aparece como un operando de un operador que espera una expresión de una categoría de valor diferente:

  • Siempre que un gl-valor aparece como un operando de un operador que requiere un pr-valor para ese operando, se aplican las conversiones estándar de l-valor a r-valor, array a puntero o función a puntero para convertir la expresión en un pr-valor.
  • A menos que se especifique lo contrario, siempre que un pr-valor aparece como un operando de un operador que espera un gl-valor para ese operando, se aplica la conversión de materialización temporal para convertir la expresión en un x-valor.
(desde C++17)

[editar] Conversión de l-valor a r-valor

Un l-valor (hasta C++11)Un gl-valor (desde C++11) de cualquier tipo T no función, no tipo array, se puede convertir implícitamente en un r-valor (hasta C++11)un pr-valor (desde C++11):

  • Si T no es un tipo clase, el tipo de r-valor (hasta C++11)pr-valor (desde C++11) es la versión no calificada-cv de T.
  • De lo contrario, el tipo de r-valor (hasta C++11)pr-valor (desde C++11) es T.

Si un programa requiere una conversión de l-valor a r-valor de un tipo incompleto, el programa está mal formado.

Cuando se produce una conversión de l-valor a r-valor dentro del operando de sizeof, no se accede al valor contenido en el objeto referenciado, ya que ese operador no evalúa su operando.

(hasta C++11)

Cuando se aplica una conversión de l-valor a r-valor a una expresión E, no se accede al valor contenido en el objeto referenciado si:

(desde C++11)


El resultado de la conversión es el valor contenido en el objeto indicado por el l-valor.

(hasta C++11)

El resultado de la conversión se determina de acuerdo con las siguientes reglas:

  • Si T es (posiblemente calificado-cv) std::nullptr_t, el resultado es una constante de puntero nulo. La conversión no accede al objeto al que hace referencia el gl-valor, por lo que no hay efectos secundarios incluso si T está calificado como volátil y el gl-valor puede hacer referencia a un miembro inactivo de una unión.
  • De lo contrario, si T tiene un tipo clase,
  • la conversión inicializa por copia un temporal del tipo T a partir del gl-valor, y el resultado de la conversión es un pr-valor para el temporal.
(hasta C++17)
(desde C++17)
  • De lo contrario, si el objeto al que hace referencia el gl-valor contiene un valor de puntero no válido, el comportamiento está definido por la implementación.
  • De lo contrario, se lee el objeto indicado por el gl-valor, y (desde C++20) el resultado es el valor contenido en el objeto.
(desde C++26)
(desde C++11)

Esta conversión modela el acto de leer un valor desde una ubicación de memoria en un registro de CPU.

[editar] Conversión de array a puntero

Un l-valor o r-valor de tipo "array de N T" o "array de límite desconocido de T" puede convertirse implícitamente a un pr-valor de tipo "puntero a T". Si el array es un pr-valor, ocurre la materialización temporal. (desde C++17) El puntero resultante se refiere al primer elemento del array (véase Decadencia de array a puntero para más detalles).

[editar] Conversión de función a puntero

Un l-valor de un tipo función puede convertirse implícitamente a un pr-valor de puntero a esa función. Esto no se aplica a funciones miembro no estáticas porque los l-valores que se refieren a las funciones miembro no estáticas no existen.

Materialización temporal

Un pr-valor de cualquier tipo completo T puede convertirse a un x-valor del mismo tipo T. Esta conversión inicializa un objeto temporal de tipo T a partir del pr-valor al evaluar el pr-valor con el objeto temporal como su objeto resultado, y produce un x-valor que denota el objeto temporal.

Si T es un tipo clase o tipo array de clase, debe tener un destructor accessible y no marcado como eliminado con = delete.

struct S { int m; };
int i = S().m; // acceso a miembro espera un gl-valor a partir de C++17;
               // S() pr-valor se convierte a x-valor

La materialización temporal ocurre en las siguientes situaciones:

Observa que la materialización temporal no ocurre cuando se inicializa un objeto desde un pr-valor del mismo tipo (por inicialización directa o inicialización de copia): tal objeto se inicializa directamente desde el inicializador. Esto garantiza la "elisión de copia obligatoria".

(desde C++17)

[editar] Promociones de enteros

Los pr-valores de tipos enteros pequeños (como char) y tipos enumeración sin ámbito pueden convertirse a pr-valores de tipos enteros más grandes (como int). En particular, los operadores aritméticos no aceptan tipos más pequeños que int como argumentos, y las promociones de enteros se aplican automáticamente después de una conversión de l-valor a r-valor, si se aplica. Esta conversión siempre conserva el valor.

Las siguientes conversiones implícitas se clasifican como promociones de enteros.

Ten en cuenta que, para un tipo de origen determinado, el tipo de destino de la promoción de entero es única y todas las demás conversiones no son promociones. Por ejemplo, la resolución de sobrecarga elige char -> int (promoción) en lugar de char -> short (conversión).

[editar] Promoción a partir de tipos enteros

Un pr-valor de tipo bool se puede convertir a un pr-valor de tipo int, donde false se convierte en 0 y true se convierte en 1.

Para un pr-valor val de un tipo entero T excepto bool:

1) Si val es el resultado de una conversión de l-valor a r-valor aplicada a un campo de bits,
  • val se puede convertir a un pr-valor de tipo int si int puede representar todos los valores del campo de bits;
  • de lo contrario, val se puede convertir a unsigned int si unsigned int puede representar todos los valores del campo de bits;
  • de lo contrario, val se puede convertir de acuerdo con las reglas especificadas en el punto (3).
2) De lo contrario (val no se convierte desde un campo de bits),
  • si T es char8_t, (desde C++20)char16_t, char32_t o (desde C++11)wchar_t, val se puede convertir de acuerdo con las reglas especificadas en el elemento (3);
  • de lo contrario, si el rango de conversión de enteros de T es inferior al rango de int:
  • val se puede convertir en un pr-valor de tipo int si int puede representar todos los valores de T;
  • de lo contrario, val se puede convertir en un pr-valor de tipo unsigned int.
3) En los casos especificados por el elemento (1) (un campo de bits convertido que no encaja en unsigned int) o el elemento (2) (T es uno de los tipos de caracteres dados), val se puede convertir en un pr-valor del primero de los siguientes tipos que pueden representar todos los valores de su tipo subyacente:
  • int
  • unsigned int
  • long
  • unsigned long
  • long long
  • unsigned long long
  • el tipo subyacente de T
(desde C++11)

[editar] Promoción a partir de tipos enumeración

Un pr-valor de un tipo enumeración sin ámbito cuyo tipo subyacente no es fijo se puede convertir en un pr-valor del primer tipo de la siguiente lista capaz de contener todo su rango de valores:

  • int
  • unsigned int
  • long
  • unsigned long
  • su rango de conversión de enteros es mayor que el rango de long long,
  • su rango de conversión de enteros es el más bajo entre todos los tipos enteros extendidos, y
  • tiene signo si hay dos tipos con el rango de conversión de enteros más bajo entre todos los tipos enteros extendidos.
(desde C++11)


Un tipo enumeración sin ámbito cuyo tipo subyacente está fijo puede convertirse a su tipo subyacente. Además, si el tipo subyacente también está sujeto a una promoción de entero, al tipo promocionado del tipo subyacente. La conversión al tipo subyacente no promocionado es mejor para propósitos de resolución de sobrecarga.

(desde C++11)

[editar] Promociones de punto flotante

Un pr-valor de tipo float puede convertirse a un pr-valor de tipo double. El valor no cambia.

Esta conversión se llama promoción de punto flotante.

[editar] Conversiones numéricas

A diferencia de las promociones, las conversiones numéricas pueden cambiar los valores, con la posible pérdida de precisión.

[editar] Conversiones de enteros

Un pr-valor de un tipo entero o de un tipo enumeración sin ámbito se puede convertir a cualquier otro tipo entero. Si la conversión se encuentra dentro de las promociones de entero, es una promoción y no una conversión.

  • Si el tipo de destino es sin signo, el valor resultante es el valor sin signo más pequeño igual al valor fuente módulo 2n
    donde n es el número de bits para representar el tipo de destino.
  • Es decir, dependiendo si el tipo de destino es más ancho o más estrecho, se extiende el signo a los enteros con signo[1] o son truncados, y se extiende el cero a los enteros sin signo o son truncados, respectivamente.
  • Si el tipo de destino tiene signo, el valor no caambia si el entero fuente puede representarse en el tipo de destino. De otra manera el resultado está definido por la implementación (hasta C++20) el valor único del tipo de destino de igual al módulo del valor fuente 2n
    donde n es el número de bits usado para representar el tipo de destino.
    (desde C++20)
    . (Ten en cuenta que esto es diferente del desborde aritmético de enteros con signo, que no está definido).
  • Si el tipo fuente es bool, el valor false se convierte a cero y el valor true se convierte al valor uno del tipo de destino (ten en cuenta que si el tipo de destino es int, esto es una promoción de entero, no una conversión de entero).
  • Si el tipo de destino es bool, esto es una conversión booleana (véase abajo).
  1. Esto solamente se aplica si la aritmética es de complemento a dos, que se requiere solamente por los tipos enteros de anchura exacta. Ten en cuenta, sin embargo, que en este momento todas las plataformas con un compilador de C++ utilizan aritmética de complemento a dos

[editar] Conversiones de punto flotante

Un pr-valor de un tipo de punto flotante se puede convertir en un pr-valor de cualquier otro tipo de punto flotante.

(hasta C++23)

Un pr-valor de un tipo de punto flotante se puede convertir en un pr-valor de cualquier otro tipo de punto flotante con un rango de conversión de punto flotante mayor o igual.

Un pr-valor de un tipo de punto flotante estándar se puede convertir a un pr-valor de cualquier otro tipo de punto flotante estándar.

static_cast se puede utilizar para convertir explícitamente un pr-valor de tipo de punto flotante a cualquier otro tipo de punto flotante.

(desde C++23)

Si la conversión se encuentra dentro de las promociones de punto flotante, es una promoción y no una conversión.

  • Si el valor fuente se puede representar exactamente en el tipo de destino, no cambia.
  • Si el valor fuente está entre dos valores representables del tipo de destino, el resultado es uno de esos dos valores (está definido por la implementación cuál de los dos, aunque si se admite la aritmética de la IEEE, el redondeo por defecto es al más cercano).
  • De lo contrario, el comportamiento no está definido.

[editar] Conversiones entre tipos de punto flotante y enteros

Un pr-valor de tipo de punto flotante se puede convertir en un pr-valor de cualquier tipo entero. La parte fraccionaria se trunca, es decir, la parte fraccionaria se descarta.

  • Si el valor no puede encajar en el tipo de destino, el comportamiento no está definido (aun cuando el tipo de destino es sin signo, la aritmética de módulo no se aplica).
  • Si el tipo de destino es bool, esto es una conversión booleana (véase abajo).

Un pr-valor de tipo entero o de tipo enumeración sin ámbito se puede convertir en un pr-valor de cualquier tipo de punto flotante.

  • Si el valor no puede ser representado correctamente, está definido por la implementación si el valor cercano más alto o el valor cercano más bajo será seleccionado, aunque si se admite la aritmética de la IEEE, el redondeo por defecto es al más cercano.
  • Si el valor no puede encajar en el tipo de destino, el comportamiento no está definido.
  • Si el tipo fuente es bool, el valor false se convierte a cero, y el valor true se convierte a uno.

[editar] Conversiones de punteros

Una constante de puntero nulo (véase NULL) puede convertirse a cualquier tipo de puntero, y el resultado es el valor del puntero nulo de ese tipo. Tal conversión (conocida como la conversión de puntero nulo) se permite para convertir un tipo calificado-cv como una sola conversión, es decir, no se considera una combinación de conversiones numéricas y calificadas.

Un puntero de pr-valor a cualquier (opcionalmente calificado-cv) tipo objeto T puede convertirse a un puntero de pr-valor a (idénticamente calificado-cv) void. El puntero resultante representa la misma ubicación en memoria que el valor del puntero original.

  • Si el puntero original es un valor de puntero nulo, el resultado es el valor del puntero nulo del tipo de destino.

Un pr-valor ptr de tipo “puntero a (posiblemente calificado-cv) Derived” se puede convertir a un pr-valor de tipo “puntero a (posiblemente calificado-cv) Base”, donde Base es una clase base de Derived, y Derived es un tipo clase completo. Si Base es inaccesible o ambiguo, el programa está mal formado.

  • Si ptr es un valor de puntero nulo, el resultado también es un valor de puntero nulo.
  • De lo contrario, si Base es una clase base virtual de Derived y ptr no apunta a un objeto cuyo tipo sea similar a Derived y que esté dentro de su tiempo de vida o dentro de su período de construcción o destrucción, el comportamiento no está definido.
  • De lo contrario, el resultado es un puntero al subobjeto de la clase base del objeto de la clase derivada.

[editar] Conversiones de puntero a miembro

  • Una constante de puntero nulo puede convertirse a cualquier tipo de puntero a miembro, y el resultado es el valor de puntero a miembro nulo de tal tipo. Dicha conversión (conocida como la conversión de puntero a miembro nulo) se permite para convertir un tipo calificado-cv como una sola conversión, es decir, no se considera una combinación de conversiones numéricas y calificadas.

Un pr-valor de tipo “puntero a miembro de Base de tipo (posiblemente calificado-cv) T” se puede convertir en un pr-valor de tipo “puntero a miembro de Derived de tipo (idénticamente calificado-cv) T”, donde Base es una clase base de Derived y Derived es un tipo clase completo. Si Base es inaccesible, ambiguo o base virtual de Derived o es una base de alguna base virtual intermedia de Derived, el programa está mal formado.

  • Si Derived no contiene el miembro original y no es una clase base de la clase que contiene el miembro original, el comportamiento no está definido.
  • De lo contrario, el puntero resultante se puede desreferenciar con un objeto Derived y accederá al miembro dentro del subobjeto base Base de ese objeto Derived.

[editar] Conversiones booleanas

Un pr-valor de tipos entero, punto flotante, enumeración sin ámbito, puntero, y puntero a miembro puede convertirse a un pr-valor de tipo bool.

El valor cero (para tipos enteros, de punto flotante y enumeraciones sin ámbito), y los valores del puntero nulo y el puntero a miembro nulo se convierten en false. Todos los otros valores se convierten en true.

En el contexto de una inicialización directa, un objeto bool puede inicializarse desde un pr-valor de tipo std::nullptr_t, incluyendo nullptr. El valor resultante es false. Sin embargo, esto no se considera una conversión implícita.

(desde C++11)

[editar] Conversiones de calificación

En términos generales:

  • Un pr-valor de tipo puntero al tipo calificado-cv T puede convertirse a un puntero de pr-valor a un mismo tipo más calificado-cv T. En otras palabras, se puede añadir, constantitud (const) y volatilidad (volatile).
  • Un pr-valor de tipo puntero a miembro de un tipo calificado-cv T en la clase X puede convertirse a un puntero de pr-valor a miembro de un tipo más calificado-cv T en la clase X.

La definición formal de “conversión de calificaciones se da en a continuación.

[editar] Tipos similares

De manera informal, dos tipos son similares si, ignorando la calificación-cv de nivel superior:

  • son del mismo tipo; o
  • ambos son punteros y los tipos a los que apuntan son similares; o
  • ambos son punteros a miembro de la misma clase y los tipos de los miembros a los que apuntan son similares; o
  • ambos son arrays y los tipos de elementos del array son similares.

Por ejemplo:

  • const int* const * y int** son similares;
  • int (*)(int*) y int (*)(const int*) no son similares;
  • const int (*)(int*) y int (*)(int*) no son similares;
  • int (*)(int* const) y int (*)(int*) son similares (son del mismo tipo);
  • std::pair<int, int> y std::pair<const int, int> no son similares.

Formalmente, la similitud de tipos se define en términos de descomposición de calificación.

Una descomposición de calificación de un tipo T es una secuencia de componentes cv_i y P_i tal que T es “cv_0 P_0 cv_1 P_1 ... cv_n−1 P_n−1 cv_n U” para n no negativo, donde

  • cada cv_i es un conjunto de const y volatile, y
  • cada P_i es
  • “puntero a”,
  • “puntero a miembro de la clase C_i de tipo”,
  • array de N_i”, o
  • array de límite desconocido de”.

Si P_i designa un array, los calificadores-cv cv_i+1 del tipo de elemento también se toman como los calificadores-cv cv_i del array.

// T es un puntero a puntero a const int”, tiene 3 descomposiciones de calificación:
// n = 0 -> cv_0 está vacío, U es “puntero a puntero a const int”
// n = 1 -> cv_0 está vacío, P_0 es “puntero a”,
//          cv_1 está vacío, U es “puntero a const int”
// n = 2 -> cv_0 está vacío, P_0 es “puntero a”,
//          cv_1 está vacío, P_1 es “puntero a”,
//          cv_2 es “const", U es “int”
using T = const int**;
 
// Sustituir cualquiera de los siguientes tipos en U da una de las descomposiciones:
// U = U0 -> la descomposición con n = 0: U0
// U = U1 -> la descomposición con n = 1: puntero a [U1]
// U = U2 -> la descomposición con n = 2: puntero a [puntero a [const U2]]
using U2 = int;
using U1 = const U2*;
using U0 = U1*;

Dos tipos T1 y T2 son similares si existe una descomposición de calificación para cada uno de ellos, donde se satisfacen todas las siguientes condiciones para las dos descomposiciones de calificación:

  • Tienen la misma n.
  • Los tipos denotados por U son los mismos.
  • Los componentes correspondientes de P_i son los mismos o uno es array de N_i” y el otro es array de límite desconocido de” para toda i.
// la descomposición de calificación con n = 2:
// puntero a [volatile puntero a [const int]]
using T1 = const int* volatile *;
 
// la descomposición de calificación con n = 2:
// const puntero a [puntero a [int]]
using T2 = int** const;
 
// Para las dos descomposiciones de calificación anteriores,
// aunque cv_0, cv_1 y cv_2 son todos diferentes,
// tienen el mismo n, U, P_0 y P_1,
// por lo tanto, los tipos T1 y T2 son similares.

[editar] Combinación de calificaciones-cv

En la descripción que aparece a continuación, la descomposición de calificación más larga del tipo Tn se denota como Dn, y sus componentes se denotan como cvn_i y Pn_i.

Una expresión pr-valor del tipo T1 se puede convertir al tipo T2 si se cumplen todas las condiciones siguientes:

  • T1 y T2 son similares.
  • Para cada i distinta de cero, si const está en cv1_i, entonces const también está en cv2_i, y lo mismo ocurre con volatile.
  • Para cada i distinta de cero, si cv1_i y cv2_i son diferentes, entonces const está en cv2_k para cada k en [1i).

El tipo de calificación combinada de dos tipos T1 y T2 es un tipo T3 similar a T1 tal que

  • cv3_0 está vacío,
  • para cada i distinta de cero, cv3_i es la unión de cv1_i y cv2_i, y
  • si cv3_i es diferente de cv1_i o c2_i, entonces const se suma a cv3_k para cada k en [1i).
(hasta C++20)

El tipo de calificación combinada de dos tipos T1 y T2 es un tipo T3 similar a T1, donde D3 satisface todas las condiciones siguientes:

  • cv3_0 está vacío.
  • Para cada i distinta de cero, cv3_i es la unión de cv1_i y cv2_i.
  • Si P1_i o P2_i es “array de límite desconocido de”, P3_i es “array de límite desconocido de”, de lo contrario es P1_i.
  • Si cv3_i es diferente de cv1_i o cv2_i, o P3_i es diferente de P1_i o P2_i, entonces se suma const a cv3_k por cada k en [1i).

Un pr-valor de tipo T1 se puede convertir al tipo T2 si el tipo de calificación combinada de T1 y T2 es T2 sin calificación-cv.

(desde C++20)
// descomposición de calificación más larga T1 (n = 2):
// puntero a [puntero a [char]]
using T1 = char**;
 
// descomposición de calificación más larga T2 (n = 2):
// puntero a [puntero a [const char]]
using T2 = const char**;
 
// Determining the cv3_i and T_i components of D3 (n = 2):
// cv3_1 = vacío (unión de cv1_1 vacío y cv2_1 vacío)
// cv3_2 = “const” (unión de cv1_2 vacío y “const” cv2_2 vacío)
// P3_0 = “puntero a” (no array de límite desconocido, usar P1_0)
// P3_1 = “puntero a” (no array de límite desconocido, usar P1_1)
// Todos los componentes excepto cv_2 son iguales, cv3_2 es ​​diferente de cv1_2,
// por lo tanto, agregar “const” a cv3_k para cada k en [1, 2): cv3_1 se convierte en “const”.
// T3 es “puntero a puntero const a const char”, es decir, const char* const *.
using T3 = /* el tipo de calificación combinada de T1 y T2 */;
 
int main()
{
    const char c = 'c';
    char* pc;
    T1 ppc = &pc;
    T2 pcc = ppc; // ERROR: T3 no es el mismo que T2 no calificado-cv,
                  //        no hay conversión explícita.
 
    *pcc = &c;
    *pc = 'C';    // Si se permite la asignación errónea anterior,
                  // el objeto constante “c” puede modificarse.
}

Ten en cuenta que en el lenguaje de programación C, const/volatile se puede agregar solo al primer nivel:

char** p = 0;
char * const* p1 = p;       // OK en C y C++
const char* const * p2 = p; // error en C, OK en C++

Conversiones de puntero a función

  • Un pr-valor de tipo puntero a una función que no lanza excepciones puede convertirse a un puntero de r-valor a una función que potencialmente lanza excepciones.
  • Un pr-valor de tipo puntero a una función miembro que no lanza excepciones puede convertirse a un puntero de pr-valor a una función miembro que potencialmente lanza excepciones.
void (*p)();
void (**pp)() noexcept = &p; // ERROR: no se puede convertir a puntero a función noexcept
 
struct S
{
    typedef void (*p)();
    operator p();
};
void (*q)() noexcept = S(); // ERROR: no se puede convertir a puntero a función noexcept
(desde C++17)

[editar] El problema del booleano seguro

Hasta la introducción de funciones de conversión explícitas en C++11, diseñar una clase que debía ser utilizable en contextos booleanos (p. ej., if (obj) { ... }) presentaba un problema: dada una función de conversión definida por el usuario, tal como T::operator bool() const;, la secuencia de conversión implícita permitía una secuencia de conversión estándar de esa llamada de función, lo que significaba que el bool resultante podía convertirse a int, permitiendo código como obj << 1; o int i = obj;.

Una solución inicial para esto se puede ver en std::basic_ios, que define operator void*(), de modo que código como if (std::cin) {...} se compila porque void* se puede convertir a bool, pero int n = std::cout; no se compila porque void* no se puede convertir a int. Esto todavía permite que código sin sentido como delete std::cout; se compile.

Muchas bibliotecas de terceros que predecieron a C++11 fueron diseñadas con una solución más elaborada, conocida como el modismo del booleano seguro. std::basic_ios también permitía este modismo a través de Asunto LWG 468, y se reemplazó operator void* (véase la sección notas).

Desde C++11, también se puede usar la conversión explícita de bool para resolver el problema del booleano seguro.

[editar] Informes de defectos

Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.

ID Aplicado a Comportamiento según lo publicado Comportamiento correcto
CWG 170 C++98 El comportamiento de las conversiones de puntero a miembro no estaba claro
si la clase derivada no tiene el miembro original.
Se aclaró.
CWG 172 C++98 El tipo enumeración se promovía en función de su tipo subyacente. En su lugar, en función de su rango de valores.
N4261 C++98 La conversión de double* const (*p)[3]
a double const * const (*p)[3] no era válida.
Se hizo válida.
CWG 519 C++98 no se garantizaba que los valores de puntero nulos
se conservaran al convertirlos a otro tipo puntero.
Siempre se conservan.
CWG 616 C++98 El comportamiento de la conversión de l-valor a r-valor de cualquier objeto no inicializado
y objetos puntero de valores no válidos siempre estaba indefinido.
Se permite un unsigned char
indeterminado; el uso de punteros no válidos
está definido por la implementación.
CWG 685 C++98 El tipo subyacente de un tipo enumeración
no se priorizaba en la promoción de entero si era fijo.
Se prioriza.
CWG 707 C++98 La conversión de entero a punto flotante
tenía un comportamiento definido en todos los casos.
El comportamiento no está definido si
el valor que se está convirtiendo está
fuera del rango de destino.
CWG 1423 C++11 std::nullptr_t era convertible a bool
tanto en la inicialización directa como en la de copia.
Solo en la inicialización directa.
CWG 1773 C++11 Una expresión de nombre que aparece en una expresión potencialmente evaluada de modo
que el objeto nombrado no sea en uso-ODR podría
aún evaluarse durante una conversión de l-valor a r-valor.
No se evalúa.
CWG 1781 C++11 La conversión de std::nullptr_t a bool se consideraba una conversión implícita
aunque solo sea válida para la inicialización directa.
Ya no se considera una conversión implícita.
CWG 1787 C++98 El comportamiento de leer desde un unsigned char indeterminado
almacenado en caché en un registro no estaba definido.
Se hizo bien definido.
CWG 1981 C++11 Las conversiones contextuales se consideraban funciones de conversión explícitas. No se consideran.
CWG 2140 C++11 No estaba claro si las conversiones de l-valor a r-valor de expresiones l-valores de
std::nullptr_t obtenían estas expresiones l-valores de la memoria.
No se obtienen de memoria.
CWG 2310 C++98 para las conversiones de puntero derivado a base y las conversiones de puntero a miembro
de base a derivado,
el tipo de clase derivada podría estar incompleto.
Debe ser completo.
CWG 2484 C++20 char8_t y char16_t tenían diferentes estrategias de promoción de enteros, pero podían
encajar en ambas.
char8_t debe promocionarse de la misma forma que char16_t.
CWG 2485 C++98 Las promociones de enteros que involucran campos de bits no se especificaban bien. Se mejoró la especificación.
CWG 2813 C++23 La materialización temporal ocurriría cuando se invoca una función miembro
de objeto explícita de una clase pr-valor.
No ocurre
en este caso.
CWG 2861 C++98 Un puntero a un objeto de tipo inaccesible podría ser
convertido en un puntero a un subobjeto de clase base.
El comportamiento no
está definido en este caso.
CWG 2879 C++17 Se aplicó la conversión de materialización temporal sobre una expresión pr-valor
como un operando de un operador que espera una expresión gl-valor.
No se aplica en algunos casos.

[editar] Véase también

Documentación de C para Conversiones implícitas