Cómo comparar dos secuencias de conversión estándar usando el rango de conversiones contenidas

Dec 11 2020
#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 ambiguousdiagnó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é #3es 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 conversiones 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 conversionof S2es una subsecuencia adecuada de Array-to-pointer conversionof 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

2 jackX Dec 11 2020 at 16:02

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 conversiony 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 conversionse considera que tiene un rango peor que el de identity conversion. Entonces, void f(int*)ganó la competencia.

cigien Dec 11 2020 at 16:14

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 fo 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, T1es int *y T2es int const *, y T1se puede convertir T2mediante 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 .

fSin 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.