Considere el siguiente programa corto de C ++:
#include <iostream>
class B {
public:
operator bool() const {
return false;
}
};
class B2 : public B {
public:
operator int() {
return 5;
}
};
int main() {
B2 b;
std::cout << std::boolalpha << (bool)b << std::endl;
}
Si lo compilo en diferentes compiladores, obtengo varios resultados. Con Clang 3.4 y GCC 4.4.7 imprime true
, mientras que Visual Studio 2013 imprime false
, lo que significa que llaman a diferentes operadores de transmisión en (bool)b
. ¿Cuál es el comportamiento correcto según el estándar?
En mi entendimiento operator bool()
no necesita ninguna conversión, mientras que operator int()
requeriría una int
a bool
la conversión, por lo que el compilador debe elegir la primera. ¿Hace const
algo con eso? ¿El compilador considera la conversión constante más "cara"?
Si elimino el const
, todos los compiladores producen igualmente false
como salida. Por otro lado, si combino las dos clases juntas (ambos operadores estarán en la misma clase), los tres compiladores producirán true
resultados.
fuente
const
es la clave aquí. Ambos operadores sean igualmente constantes o no, se comportan como se esperaba, labool
versión "gana". Con diferente consistencia, siempre la versión no constante "gana" en cada plataforma. Con clases derivadas y diferente consistencia de los operadores, VS parece tener un error ya que se comporta de manera diferente a los otros dos compiladores.true
. Si GCC, Clang y EDG están de acuerdo y MSVC no está de acuerdo, eso generalmente significa que MSVC está equivocado.Respuestas:
Los estados estándar:
Lo que significa que
operator bool
no está oculto poroperator int
.Los estados estándar:
El "argumento de objeto implícito" en este caso es
b
, que es de tipoB2 &
.operator bool
requiereconst B2 &
, por lo que el compilador tendrá que agregar constb
para llamaroperator bool
. Esto, en igualdad de condiciones, haceoperator int
una mejor combinación.El estándar establece que un
static_cast
(que el reparto de estilo C está realizando en este caso) puede convertirse en un tipoT
(en este casoint
) si:Por lo tanto,
int
se puede convertir a abool
, y abool
se puede convertir igualmente en abool
.Los estados estándar:
Entonces, el conjunto de sobrecarga consta de
operator int
yoperator bool
. En igualdad de condiciones,operator int
es una mejor combinación (ya que no tiene que agregar constness). Poroperator int
lo tanto, debe seleccionarse.Tenga en cuenta que (quizás en contra de la intuición) el estándar no considera el tipo de retorno (es decir, el tipo al que se convierten estos operadores) una vez que se han agregado al conjunto de sobrecarga (como se estableció anteriormente), siempre que la secuencia de conversión para los argumentos de uno de ellos es superior a la secuencia de conversión para los argumentos del otro (que, debido a la constancia, es el caso en este caso).
Los estados estándar:
En este caso, solo hay un argumento (el
this
parámetro implícito ). La secuencia de conversión paraB2 &
=>B2 &
(llamaroperator int
) es superior aB2 &
=>const B2 &
(llamaroperator bool
) y, poroperator int
lo tanto, se selecciona del conjunto de sobrecarga sin tener en cuenta el hecho de que en realidad no se convierte directamente enbool
.fuente
Corto
La función de conversión
operator int()
se selecciona mediante clang overoperator bool() const
yab
que no está calificado const, mientras que el operador de conversión para bool sí lo es.El corto razonamiento es que las funciones candidatas para la resolución de sobrecarga (con parámetro de objeto implícita en su lugar), al convertir
b
abool
sonoperator bool (B2 const &); operator int (B2 &);
donde el segundo es un mejor partido ya
b
que no está calificado const.Si ambas funciones comparten la misma calificación (ambas
const
o no),operator bool
se selecciona ya que proporciona conversión directa.Conversión mediante notación emitida, analizada paso a paso
Si estamos de acuerdo en que el inserter booleano ostream (std :: basic_ostream :: operator << (bool val) según [ostream.inserters.arithmetic]) se llama con el valor que resulta de una conversión de
b
abool
, podemos profundizar en esa conversión .1. La expresión del elenco
El elenco de b to bool
(bool)b
evalúa a
static_cast<bool>(b)
según C ++ 11, 5.4 / 4 [expr.cast] desde
const_cast
que no es aplicable (no se agrega ni se elimina const aquí).Esta conversión estática está permitida por C ++ 11, 5.2.9 / 4 [expr.static.cast] , si
bool t(b);
para una variable inventada t está bien formado. Tales declaraciones se denominan inicialización directa según C ++ 11, 8.5 / 15 [dcl.init] .2. Inicialización directa
bool t(b);
La cláusula 16 del párrafo estándar menos mencionado establece (el énfasis es mío):
2.1 ¿Qué funciones de conversión están disponibles?
Las funciones de conversión disponibles son
operator int ()
yoperator bool() const
como C ++ 11, 12.3 / 5 [class.conv] nos dice:Mientras que C ++ 11, 13.3.1.5/1 [over.match.conv] dice:
donde S es la clase desde la que se convertirá.
2.2 ¿Qué funciones de conversión son aplicables?
C ++ 11, 13.3.1.5/1 [over.match.conv] (el énfasis es mío):
Por
operator bool () const
lo tanto, es aplicable ya que no se esconde dentroB2
y produce abool
.La parte con énfasis en la última cotización estándar es relevante para la conversión usando
operator int ()
ya queint
es un tipo que se puede convertir a bool mediante la secuencia de conversión estándar. La conversión deint
abool
ni siquiera es una secuencia, sino una conversión directa simple que está permitida por C ++ 11, 4.12 / 1 [conv.bool]Esto significa que
operator int ()
es aplicable.2.3 ¿Qué función de conversión se selecciona?
La selección de la función de conversión adecuada se realiza mediante resolución de sobrecarga ( C ++ 11, 13.3.1.5/1 [over.match.conv] ):
Hay una "peculiaridad" especial cuando se trata de la resolución de sobrecargas para funciones miembro de clase: el parámetro de objeto implícito ".
Según C ++ 11, 13.3.1 [over.match.funcs] ,
donde el tipo de este parámetro para funciones miembro no estáticas, de acuerdo con la cláusula 4, es:
Esto significa que (según C ++ 11, 13.3.1.5/2 [over.match.conv] ), en una inicialización por función de conversión,
Las funciones candidatas para la resolución de sobrecargas son:
operator bool (B2 const &); operator int (B2 &);
Obviamente,
operator int ()
es una mejor coincidencia si se solicita una conversión utilizando un objeto de tipo no constanteB2
ya queoperator bool ()
requiere una conversión de calificación.Si ambas funciones de conversión comparten la misma calificación const, la resolución de sobrecarga de esas funciones ya no funcionará. En este caso, entra en juego la clasificación de conversión (secuencia).
3. ¿Por qué se
operator bool ()
selecciona cuando ambas funciones de conversión comparten la misma calificación constante?La conversión de
B2
abool
es una secuencia de conversión definida por el usuario ( C ++ 11, 13.3.3.1.2 / 1 [over.ics.user] )C ++ 11, 13.3.3.2/3 [over.ics.rank]
La segunda conversión estándar es el caso de
operator bool()
esbool
abool
(conversión de identidad) mientras que la segunda conversión estándar en el caso deoperator int ()
esint
abool
es una conversión booleana.Por lo tanto, la secuencia de conversión, usando
operator bool ()
, es mejor si ambas funciones de conversión comparten la misma calificación constante.fuente
a/b
es la misma si el código esfloat f = a/b;
oint f = a/b;
. Pero este es un caso en el que puede resultar un poco sorprendente.El tipo bool de C ++ tiene dos valores: verdadero y falso con los valores correspondientes 1 y 0. La confusión inherente se puede evitar si agrega un operador bool en la clase B2 que llama al operador bool de la clase base (B) explícitamente, entonces la salida viene como falso. Aquí está mi programa modificado. Entonces operador bool significa operador bool y no operador int de ninguna manera.
#include <iostream> class B { public: operator bool() const { return false; } }; class B2 : public B { public: operator int() { return 5; } operator bool() { return B::operator bool(); } }; int main() { B2 b; std::cout << std::boolalpha << (bool)b << std::endl; }
En su ejemplo, (bool) b estaba tratando de llamar al operador bool para B2, B2 ha heredado el operador bool y el operador int, por regla de dominio, se llama al operador int y al operador bool heredado en B2. Sin embargo, al tener explícitamente un operador bool en la propia clase B2, el problema se resuelve.
fuente
true and false with corresponding values 1 and 0
Eso es una simplificación excesiva.true
convierte a entero1
, pero eso no significa que "tiene" el valor1
. De hecho,true
puede estar42
debajo.bool
nunca tiene el valor 0 o 1; los únicos valores que puede tomar sonfalse
ytrue
(que se convierten en 0 y 1 sibool
se convierte en un tipo integral).Algunas de las respuestas anteriores ya brindan mucha información.
Mi contribución es que las "operaciones de conversión" se compilan de manera similar, a las "operaciones sobrecargadas", sugiero hacer una función con un identificador único para cada operación y luego reemplazarla por el operador requerido o conversión.
#include <iostream> class B { public: bool ToBool() const { return false; } }; class B2 : public B { public: int ToInt() { return 5; } }; int main() { B2 b; std::cout << std::boolalpha << b.ToBool() << std::endl; }
Y, posteriormente, aplicar el operador o yeso.
#include <iostream> class B { public: operator bool() { return false; } }; class B2 : public B { public: operator int() { return 5; } }; int main() { B2 b; std::cout << std::boolalpha << (bool)b << std::endl; }
Solo mis 2 centavos.
fuente