Corto
La función de conversión operator int()
se selecciona mediante clang over operator bool() const
ya b
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
a bool
son
operator 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
a bool
, 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] desdeconst_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):
La semántica de los inicializadores es la siguiente. El tipo de destino es el tipo de objeto o referencia que se inicializa y el tipo de origen es el tipo de expresión inicializadora.
[...]
[...] si el tipo de fuente es un tipo de clase (posiblemente calificado por cv) , funciones de conversión se consideran las .
Se enumeran las funciones de conversión aplicables y se elige la mejor mediante resolución de sobrecarga.
2.1 ¿Qué funciones de conversión están disponibles?
Las funciones de conversión disponibles son operator int ()
y operator bool() const
como C ++ 11, 12.3 / 5 [class.conv] nos dice:
Una función de conversión en una clase derivada no oculta una función de conversión en una clase base a menos que las dos funciones se conviertan al mismo tipo.
Mientras que C ++ 11, 13.3.1.5/1 [over.match.conv] dice:
Se consideran las funciones de conversión de S y sus clases base.
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):
1 [...] Suponiendo que "cv1 T" es el tipo de objeto que se está inicializando, y "cv S" es el tipo de expresión inicializadora, con S un tipo de clase, las funciones candidatas se seleccionan de la siguiente manera: La conversión Se consideran las funciones 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 a través de una secuencia de conversión estándar son funciones candidatas.
Por operator bool () const
lo tanto, es aplicable ya que no se esconde dentro B2
y produce a bool
.
La parte con énfasis en la última cotización estándar es relevante para la conversión usando operator int ()
ya que int
es un tipo que se puede convertir a bool mediante la secuencia de conversión estándar. La conversión de int
a bool
ni siquiera es una secuencia, sino una conversión directa simple que está permitida por C ++ 11, 4.12 / 1 [conv.bool]
Un prvalue de aritmética, enumeración sin ámbito, puntero o puntero a tipo de miembro se puede convertir en un prvalue de tipo bool. Un valor cero, un valor de puntero nulo o un valor de puntero de miembro nulo se convierte en falso; cualquier otro valor se convierte en verdadero.
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] ):
La resolución de sobrecarga se utiliza para seleccionar la función de conversión que se invocará.
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] ,
[...] las funciones miembro estáticas y no estáticas tienen un parámetro de objeto implícito [...]
donde el tipo de este parámetro para funciones miembro no estáticas, de acuerdo con la cláusula 4, es:
donde X es la clase de la que la función es miembro y cv es la calificación cv en la declaración de función miembro.
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,
[l] a lista de argumentos tiene un argumento, que es la expresión inicializadora. [Nota: Este argumento se comparará con el parámetro de objeto implícito de las funciones de conversión. —Nota final]
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 constante B2
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
a bool
es una secuencia de conversión definida por el usuario ( C ++ 11, 13.3.3.1.2 / 1 [over.ics.user] )
Una secuencia de conversión definida por el usuario consta de una secuencia de conversión estándar inicial seguida de una conversión definida por el usuario seguida de una segunda secuencia de conversión estándar.
[...] Si la conversión definida por el usuario se especifica mediante una función de conversión, la secuencia de conversión estándar inicial convierte el tipo de fuente en el parámetro de objeto implícito de la función de conversión.
C ++ 11, 13.3.3.2/3 [over.ics.rank]
[...] define un orden parcial de secuencias de conversión implícitas basadas en las relaciones mejor secuencia de conversión y mejor conversión.
[...] La secuencia de conversión definida por el usuario U1 es una secuencia de conversión mejor que otra secuencia de conversión definida por el usuario U2 si contienen la misma función de conversión definida por el usuario o constructor o inicialización agregada y la segunda secuencia de conversión estándar de U1 es mejor que la segunda secuencia de conversión estándar de U2.
La segunda conversión estándar es el caso de operator bool()
es bool
a bool
(conversión de identidad) mientras que la segunda conversión estándar en el caso de operator int ()
es int
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.