conversion casteo castear cast c++ implicit-conversion void-pointers

castear - casteo c++



C++, ¿la conversión de bool siempre vuelve a la conversión implícita a void*? (5)

Pregunta: ¿Las conversiones de bools implícitas siempre recurren al intento de conversión implícita a void* ? (Si existe tal función de conversión para el tipo). Si es así, ¿por qué?

Considere el siguiente programa corto:

#include <iostream> class Foo{ public: operator void*() const { std::cout << "operator void*() const" << std::endl; return 0; } }; int main() { Foo f; if(f) std::cout << "True" << std::endl; else std::cout << "False" << std::endl; return 0; }

La salida de este programa es:

operator void*() const False

es decir, se llamó a la función de conversión a void* . Si etiquetamos un calificador explicit delante de la función de conversión, la conversión implícita a void* fallará.

Editar: Parece que muchas respuestas son que "los punteros nulos se pueden convertir en false ". Entiendo esto, mi pregunta fue con respecto a "si no puedo llamar directamente al operator bool() , intentaré la conversión a cualquier puntero".


Cualquier operator conversión integral trabajaría de la misma manera. Devuelves 0 en tu operador, de ahí lo False .

operator [int,double,uint64_t,<any_integral>]() const { std::cout << "integral operator called" << std::endl; return 0; }

Cualquier tipo de integral puede ser usado en expresiones lógicas.


Este puede ser de cualquier tipo que se pueda usar en un contexto booleano, y void * no es especial aquí, ¿eh?


Lo que realmente sucede es que su clase tiene una conversión implícita a un tipo de puntero void* en este caso. Devuelve 0 , que es la macro NULL, que se acepta como un tipo de puntero.

Los punteros tienen conversión implícita a valores booleanos, y los punteros nulos se convierten a falso.

Realmente podrías tener una conversión implícita diferente a un puntero para Foo :

operator int*() const { std::cout << "operator int* const" << std::endl; return new int(3); }

Y tu salida cambiará a

operador int * const
Cierto

Sin embargo, si tiene ambos , entonces obtiene un error de compilación:

class Foo{ public: operator int*() const { std::cout << "operator int* const" << std::endl; return new int(3); } operator void*() const { std::cout << "operator void*() const" << std::endl; return 0; } };

main.cpp: 26: 9: error: la conversión de ''Foo'' a ''bool'' es ambigua

Sin embargo, si define explícitamente una conversión, demasiado bool, entonces no es ambiguo:

operator void*() const { std::cout << "operator void*() const" << std::endl; return 0; } operator bool() const { std::cout << "operator bool() const" << std::endl; return true; } // <--- compiler chooses this one

El tema de las conversiones implícitas es bastante interesante, ya que refleja cómo el compilador elige una función miembro apropiada para los argumentos dados (conversiones de valores, promociones integrales, etc.).

Es decir, el compilador tiene una lista de prioridades que elegirá cuando intente determinar a qué se refiere. Si dos sobrecargas tienen la misma prioridad, obtendrá un error.

Por ejemplo, siempre se elegirá el operator bool , pero si, en cambio, tuviera que elegir entre operator int y operator void* , se seleccionará operator int porque elige conversión numérica sobre conversiones de puntero.

Sin embargo, si tiene el operator int y el operator char , obtendrá un error porque ambos son conversiones integrales numéricas.


Para algunas referencias en la Norma:

  • §6.4.0.4 [stmt.select]

    El valor de una condición que es una expresión es el valor de la expresión, convertida contextualmente a bool para las declaraciones que no sean switch

  • §4.0.4 [conv]

    Ciertas construcciones de lenguaje requieren que una expresión se convierta en un valor booleano. Se dice que una expresión e aparece en dicho contexto se convierte contextualmente a bool y está bien formada si y solo si la declaración es bool t(e); Está bien formado, por alguna variable temporal inventada t .

  • §8.5.17 [dcl.init]

    La semántica de los inicializadores es la siguiente. El tipo de destino es el tipo de objeto o referencia que se está inicializando y el tipo de origen es el tipo de la expresión de inicializador.

  • §8.5.17.7 [dcl.init]

    De lo contrario, si el tipo de fuente es un tipo de clase (posiblemente cv calificado), se consideran las funciones de conversión. Las funciones de conversión aplicables se enumeran (13.3.1.5) y la mejor se elige a través de la resolución de sobrecarga (13.3). La conversión definida por el usuario así seleccionada se llama para convertir la expresión inicializadora en el objeto que se está inicializando. Si la conversión no se puede realizar o es ambigua, la inicialización no se realiza correctamente.

  • §13.3.1.5 [over.match.conv]

    Suponiendo que "cv1 T " es el tipo de objeto que se está inicializando, y "cv S " es el tipo de la expresión de inicialización, con S un tipo de clase, las funciones candidatas se seleccionan de la siguiente manera:

    Se consideran las funciones de conversión de S y sus clases base. Las funciones de conversión no explícitas que no están ocultas dentro de S y producen el tipo T o un tipo que se puede convertir al tipo T través de una secuencia de conversión estándar (13.3.3.1.1) son funciones candidatas. Para la inicialización directa, las funciones de conversión explícitas que no están ocultas dentro de S y producen el tipo T o un tipo que se puede convertir al tipo T con una conversión de calificación (4.4) también son funciones candidatas.

  • §4.13.1 [conv.bool]

    Un prvalor de aritmética, enumeración sin ámbito, puntero o puntero al tipo de miembro se puede convertir en un prvalor de tipo bool . Un valor cero, un valor de puntero nulo o un valor de puntero de miembro nulo se convierte en false ; Cualquier otro valor se convierte en true .


Si el compilador no puede convertir un tipo definido por el usuario a bool directamente, entonces trata de hacerlo indirectamente, es decir, convierte (a través de una conversión definida por el usuario) a un tipo que se puede convertir a bool sin involucrar a otra conversión definida por el usuario. La lista de dichos tipos incluye (y parece estar limitada a) los siguientes tipos:

  • un tipo aritmético entero ( char , int , etc)
  • un tipo aritmético de punto flotante ( float , double , long double )
  • un tipo de puntero ( void* pertenece aquí, pero también podría ser const std::vector<Something>* )
  • un puntero a función (incluido un puntero a una función miembro)
  • un tipo de referencia a cualquiera de los anteriores

Sin embargo, tenga en cuenta que solo debe existir una conversión indirecta de este tipo. Si son posibles dos o más conversiones de la lista anterior, entonces el compilador se enfrentará a una ambigüedad y reportará un error.