Si estan relacionados
Supongamos por un momento que en B
realidad es una base de D
. Luego, para la llamada a check
, ambas versiones son viables porque Host
se pueden convertir en D*
y B*
. Es una secuencia de conversión definida por el usuario como se describe en 13.3.3.1.2
from Host<B, D>
a D*
y B*
respectivamente. Para encontrar funciones de conversión que puedan convertir la clase, las siguientes funciones candidatas se sintetizan para la primera check
función de acuerdo con13.3.1.5/1
D* (Host<B, D>&)
La primera función de conversión no es candidata porque B*
no se puede convertir a D*
.
Para la segunda función, existen los siguientes candidatos:
B* (Host<B, D> const&)
D* (Host<B, D>&)
Esos son los dos candidatos a la función de conversión que toman el objeto host. El primero lo toma por referencia constante y el segundo no. Por lo tanto, el segundo es una mejor coincidencia para el *this
objeto no constante (el argumento del objeto implícito ) por 13.3.3.2/3b1sb4
y se usa para convertir a B*
para la segunda check
función.
Si eliminara la constante, tendríamos los siguientes candidatos
B* (Host<B, D>&)
D* (Host<B, D>&)
Esto significaría que ya no podemos seleccionar por constness. En un escenario de resolución de sobrecarga normal, la llamada ahora sería ambigua porque normalmente el tipo de retorno no participará en la resolución de sobrecarga. Para las funciones de conversión, sin embargo, existe una puerta trasera. Si dos funciones de conversión son igualmente buenas, entonces el tipo de retorno de ellas decide cuál es la mejor según 13.3.3/1
. Por lo tanto, si eliminara la constante, se tomaría la primera, porque B*
convierte mejor a B*
que D*
a B*
.
Ahora bien, ¿qué secuencia de conversión definida por el usuario es mejor? ¿El de la segunda o primera función de verificación? La regla es que las secuencias de conversión definidas por el usuario solo se pueden comparar si utilizan la misma función de conversión o constructor de acuerdo con 13.3.3.2/3b2
. Este es exactamente el caso aquí: ambos usan la segunda función de conversión. Observe que, por tanto, la constante es importante porque obliga al compilador a tomar la segunda función de conversión.
Ya que podemos compararlos, ¿cuál es mejor? La regla es que la mejor conversión del tipo de retorno de la función de conversión al tipo de destino gana (nuevamente por 13.3.3.2/3b2
). En este caso, D*
convierte mejor a D*
que a B*
. ¡Así se selecciona la primera función y reconocemos la herencia!
Tenga en cuenta que dado que nunca necesitamos realmente convertir a una clase base, podemos reconocer con ello la herencia privada , porque si podemos convertir de una D*
a una B*
no depende de la forma de herencia de acuerdo con la4.10/3
Si no están relacionados
Ahora supongamos que no están relacionados por herencia. Así, para la primera función tenemos los siguientes candidatos
D* (Host<B, D>&)
Y por el segundo ahora tenemos otro conjunto
B* (Host<B, D> const&)
Como no podemos convertir D*
a B*
si no tenemos una relación de herencia, ¡ahora no tenemos una función de conversión común entre las dos secuencias de conversión definidas por el usuario! Por tanto, seríamos ambiguos si no fuera por el hecho de que la primera función es una plantilla. Las plantillas son la segunda opción cuando hay una función que no es de plantilla que es igualmente buena de acuerdo con 13.3.3/1
. Por lo tanto, seleccionamos la función sin plantilla (la segunda) y reconocemos que no hay herencia entre B
y D
!