Cómo comparar dos secuencias de conversión estándar usando el rango de conversiones contenidas
#include <iostream>
void g(int*); //#1
void g(int (&arr)[2]); //#2
void f(int*); //#3
void f(int const*); //#4
int main(){
int arr[2] ={0};
f(arr); // choose #3
g(arr); //ambiguous
}
Considere el código anterior, # 3 se seleteced para f(ptr)
, sin embargo, g(arr)
da un ambiguous
diagnóstico.
La regla para elegir la mejor función se define como:
La secuencia de conversión estándar S1 es una secuencia de conversión mejor que la secuencia de conversión estándar S2 si
- S1 es una subsecuencia adecuada de S2 (comparando las secuencias de conversión en la forma canónica definida por [over.ics.scs], excluyendo cualquier transformación Lvalue; la secuencia de conversión de identidad se considera una subsecuencia de cualquier secuencia de conversión sin identidad ) o , si no eso
Así que echa un vistazo a over.ics.scs # 3
Estos se utilizan para clasificar las secuencias de conversión estándar. El rango de una secuencia de conversión se determina considerando el rango de cada conversión en la secuencia y el rango de cualquier unión de referencia.
De acuerdo con mi comprensión de la regla anterior, puedo entender por qué #3
es la mejor sobrecarga para f(ptr)
, es decir:
Dado S1 como (arr => int *):
Array-to-pointer conversion -> (identity conversion)
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
int[2] => int* int* => int*
mientras se da S2 como (ptr => int const *)
Array-to-pointer conversion -> Qualification conversions -> identity conversion
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
int[2] => int* int* => int const* int const* => int const*
Dado que identity conversion
es una subsecuencia adecuada de Qualification conversions
, por lo tanto, S1 es mejor que S2. Entonces, el # 3 se selecciona por resolución de sobrecarga para f(ptr)
.
Cuando utilizo un proceso similar para determinar cuál es mejor g(arr)
, encuentro un problema.
Nuevamente, dado S1 como (arr => int *)
Array-to-pointer conversion -> identity conversion
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
int[2] => int* int* => int*
mientras se da S2 como (arr => int (& arr) [2])
Cuando un parámetro de tipo de referencia se une directamente a una expresión de argumento, la secuencia de conversión implícita es la conversión de identidad, a menos que la expresión de argumento tenga un tipo que sea una clase derivada del tipo de parámetro, en cuyo caso la secuencia de conversión implícita es una derivada. conversión a base
identity conversion
^^^^^^^^^^^^^^^^^^^
bind to reference
Aquí, identity conversion
of S2
es una subsecuencia adecuada de Array-to-pointer conversion
of S1
, por lo tanto, debería ser mejor que S1
, ¿por qué el compilador se quejó de que g(arr)
es una invocación ambigua?
¿Tengo alguna mala interpretación sobre cómo clasificar las secuencias de conversión estándar? ¿Cómo comparar dos ICS estándar (rango de la conversión contenida)?
Respuestas
El punto clave está aquí:
S1 es una subsecuencia adecuada de S2 (comparando las secuencias de conversión en la forma canónica definida por [over.ics.scs], excluyendo cualquier transformación Lvalue ; la secuencia de conversión de identidad se considera una subsecuencia de cualquier secuencia de conversión sin identidad) o , si no eso
Eso significa que, para la llamada de función g(arr)
, no se utilizan todas las conversiones de matriz a puntero para determinar el rango. En otras palabras, de un tipo int[2]
a otro int*
, solo hay una conversión de identidad que solía determinar el rango. Por lo tanto, S1 de void g(int*);
y S2 de void g(int (&arr)[2]);
son ICS indistinguibles, por lo que el compilador da un error ambiguo.
Por el contrario, las conversiones de void f(int*);
y void f(int const*);
utilizadas para comparar el rango son identity conversion
y qualification conversion
, respectivamente.
En concordancia con reglas:
la secuencia de conversión de identidad se considera una subsecuencia de cualquier secuencia de conversión sin identidad
Por tanto, Qualification conversion
se considera que tiene un rango peor que el de identity conversion
. Entonces, void f(int*)
ganó la competencia.
Está intentando aplicar over.ics.rank-3.2.1 a estos conjuntos de sobrecarga, pero esta regla no se aplica a ninguno de los dos f
o g
.
Dada la llamada f(arr);
, al realizar la resolución de sobrecarga f
, ambas sobrecargas requieren una secuencia de conversión estándar que consiste en una conversión de matriz a puntero , y ambas tienen el mismo rango, que es Exact Match . El desempate utilizado en este caso es over.match.best # over.ics.rank-3.2.5 :
La secuencia de conversión estándar S1 es una secuencia de conversión mejor que la secuencia de conversión estándar S2 si
...
S1 y S2 difieren solo en su conversión de calificación ([conv.qual]) y producen tipos similares T1 y T2, respectivamente, donde T1 se puede convertir en T2 mediante una conversión de calificación.
Hay un ejemplo siguiendo esta regla que demuestra cómo funciona la regla.
Para el conjunto de sobrecarga f
, T1
es int *
y T2
es int const *
, y T1
se puede convertir T2
mediante una conversión de calificación.
En el caso de la llamada g(arr);
, al realizar la resolución de sobrecarga, la sobrecarga g(int (&)[2])
se clasifica como una coincidencia exacta , ya que la secuencia de conversión estándar necesaria es No se requiere conversión .
Sin embargo, la sobrecarga g(int*)
también se clasifica como una coincidencia exacta , ya que la secuencia de conversión estándar necesaria es una conversión de matriz a puntero .
f
Sin embargo, a diferencia de for , no hay una regla en [over.ics.rank] que desambigue entre las secuencias de conversión estándar para g
, y la llamada falla.