c++ language-lawyer implicit-conversion overload-resolution

c++ - En la resolución de sobrecarga, ¿la selección de una función que usa la secuencia de conversión ambigua necesariamente hace que la llamada no esté bien formada?



language-lawyer implicit-conversion (1)

La pregunta surgió mientras estaba investigando la respuesta a esta pregunta de SO . Considere el siguiente código:

struct A{ operator char() const{ return ''a''; } operator int() const{ return 10; } }; struct B { void operator<< (int) { } }; int main() { A a; B b; b << a; }

La conversión de a a int puede ser a través de un a.operator char() seguido de una promoción integral, o un a.operator int() seguido de una conversión de identidad (es decir, sin conversión). La norma dice que (§13.3.3.1 [over.best.ics] / p10, nota omitida, negrita, todas las citas son de N3936):

Si existen varias secuencias diferentes de conversiones que convierten el argumento al tipo de parámetro, la secuencia de conversión implícita asociada con el parámetro se define como la secuencia de conversión única designada como la secuencia de conversión ambigua . Con el fin de clasificar las secuencias de conversión implícitas como se describe en 13.3.3.2, la secuencia de conversión ambigua se trata como una secuencia definida por el usuario que es indistinguible de cualquier otra secuencia de conversión definida por el usuario. Si se selecciona una función que utiliza la secuencia de conversión ambigua como la mejor función viable, la llamada no se formará correctamente porque la conversión de uno de los argumentos de la llamada es ambigua.

Aquí, B::operator<<(int) es el único candidato viable, y por lo tanto es el mejor candidato viable, aunque la secuencia de conversión para el parámetro es la secuencia de conversión ambigua . Según la oración en negrita, entonces, la llamada debe estar mal formada porque "la conversión de uno de los argumentos en la llamada es ambigua".

Sin embargo, ningún compilador que probé (g ++, clang y MSVC) informa realmente un error, lo cual tiene sentido porque después de seleccionar la función a llamar a través de la resolución de sobrecarga, el parámetro de la función (8.3.5) se inicializará (8.5, 12.8 , 12.1) con su argumento correspondiente "(§5.2.2 [expr.call] / p4). Esta inicialización es una copia inicial (§8.5 [dcl.init] / p15), y de acuerdo con §8.5 [dcl.init] / p17, da como resultado una nueva ronda de resolución de sobrecarga para determinar la función de conversión a utilizar:

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. Si el inicializador no es una expresión única (posiblemente entre paréntesis), el tipo de fuente no está definido.

  • [...]
  • Si el tipo de destino es un tipo de clase (posiblemente cv calificado): [...]
  • 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 de inicialización en el objeto que se está inicializando. Si la conversión no se puede realizar o es ambigua, la inicialización no se ha realizado correctamente.
  • [...]

Y en esta ronda de resolución de sobrecarga, hay un desempate en §13.3.3 [over.match.best] / p1:

una función viable F1 se define como una función mejor que otra función viable F2 si, para todos los argumentos i , ICSi(F1) no es una secuencia de conversión peor que ICSi(F2) , y luego

  • para algún argumento j , ICSj(F1) es una mejor secuencia de conversión que ICSj(F2) , o, si no es así,
  • el contexto es una inicialización por conversión definida por el usuario (ver 8.5, 13.3.1.5 y 13.3.1.6) y la secuencia de conversión estándar del tipo de retorno de F1 al tipo de destino (es decir, el tipo de la entidad que se está inicializando) es una mejor secuencia de conversión que la secuencia de conversión estándar del tipo de retorno de F2 al tipo de destino.

(Ejemplo y resto de la lista omitidos)

Dado que la secuencia de conversión estándar de int a int (rango de coincidencia exacta) es mejor que la secuencia de conversión estándar de char a int (rango de promoción), la primera supera a la segunda, y no debería haber ambigüedad, la conversión definida por el operator int() se utilizará para la inicialización, que luego contradice la oración en §13.3.3.1 [over.best.ics] / p10 que dice que la llamada a la función estará mal formada debido a la ambigüedad.

¿Hay algo incorrecto en el análisis anterior, o esa frase es un error en el estándar?


Al decidir la mejor conversión definida por el usuario para una secuencia de conversión definida por el usuario, tenemos un conjunto candidato de sobrecarga.

§13.3.3 / p1 dice:

Defina ICSi (F) como sigue:

  • [...]

  • deje que ICSi (F) denote la secuencia de conversión implícita que convierte el i-th argumento en la lista al tipo del i-th parámetro de la función viable F. 13.3.3.1 define las secuencias de conversión implícita y 13.3.3.2 define lo que significa para que una secuencia de conversión implícita sea una mejor secuencia de conversión o peor secuencia de conversión que otra.

Dadas estas definiciones, una función viable F1 se define como una función mejor que otra función viable F2 si, para todos los argumentos i, ICSi (F1) no es una secuencia de conversión peor que ICSi (F2), y luego

- [...]

- el contexto es una inicialización por conversión definida por el usuario (ver 8.5, 13.3.1.5 y 13.3.1.6) y la secuencia de conversión estándar del tipo de retorno de F1 al tipo de destino (es decir, el tipo de la entidad que se está inicializando) es una mejor secuencia de conversión que la secuencia de conversión estándar del tipo de retorno de F2 al tipo de destino.

Esto se aplica desde

§13.3.3.1.2 / p2

2 La segunda secuencia de conversión estándar convierte el resultado de la conversión definida por el usuario al tipo de destino para la secuencia. Como una secuencia de conversión implícita es una inicialización, las reglas especiales para la inicialización por conversión definida por el usuario se aplican cuando se selecciona la mejor conversión definida por el usuario para una secuencia de conversión definida por el usuario (consulte 13.3.3 y 13.3.3.1).

y, por lo tanto, la secuencia de conversión que involucra al operator int se selecciona como la mejor coincidencia.

Finalmente, reformularía §13.3.3.1 / p10 como

Si existen varias secuencias diferentes de conversiones que convierten el argumento al tipo de parámetro y no fue posible determinar el mejor candidato, la secuencia de conversión implícita asociada con el parámetro se define como la secuencia de conversión única designada como la secuencia de conversión ambigua. Con el propósito de clasificar las secuencias de conversión implícitas como se describe en 13.3.3.2, la secuencia de conversión ambigua se trata como una secuencia definida por el usuario que es indistinguible de cualquier otra secuencia de conversión definida por el usuario134. Si se selecciona una función que utiliza la secuencia de conversión ambigua como la mejor función viable, la llamada no se formará correctamente porque la conversión de uno de los argumentos de la llamada es ambigua.