Como comparar duas sequências de conversão padrão, use a classificação de conversões contidas

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 o código acima, # 3 é selecionado para f(ptr), no entanto, g(arr)fornece um ambiguousdiagnóstico.

A regra para escolher a melhor função é definida como:

A sequência de conversão padrão S1 é uma sequência de conversão melhor do que a sequência de conversão padrão S2 se

  • S1 é uma subsequência adequada de S2 (comparando as sequências de conversão na forma canônica definida por [over.ics.scs], excluindo qualquer Lvalue Transformation; a sequência de conversão de identidade é considerada uma subsequência de qualquer sequência de conversão de não identidade ) ou , se não for isso

Portanto, dê uma olhada em over.ics.scs # 3

Eles são usados ​​para classificar as sequências de conversão padrão. A classificação de uma sequência de conversão é determinada considerando a classificação de cada conversão na sequência e a classificação de qualquer ligação de referência.

De acordo com o meu entendimento da regra acima, posso entender porque #3é a melhor sobrecarga para f(ptr), ou seja:

Dado S1 como (arr => int *):

Array-to-pointer conversion -> (identity conversion)  
^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^                   
     int[2] => int*             int* => int* 

enquanto dado S2 como (ptr => int const *)

Array-to-pointer conversion -> Qualification conversions ->  identity conversion   
^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^     ^^^^^^^^^^^^^^^^^^^ 
     int[2] => int*               int* => int const*           int const* => int const* 

Visto que identity conversioné uma subsequência apropriada de Qualification conversions, portanto, S1 é melhor do que S2. Portanto, # 3 é selecionado pela resolução de sobrecarga para f(ptr).

Quando uso um processo semelhante para determinar qual é o melhor g(arr), encontro um problema.

Novamente, dado S1 como (arr => int *)

Array-to-pointer conversion -> identity conversion  
^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^ 
      int[2] => int*              int* => int*

enquanto S2 é dado como (arr => int (& arr) [2])

Quando um parâmetro de tipo de referência liga-se diretamente a uma expressão de argumento, a sequência de conversão implícita é a conversão de identidade, a menos que a expressão de argumento tenha um tipo que seja uma classe derivada do tipo de parâmetro, caso em que a sequência de conversão implícita é uma classe derivada conversão para base

identity conversion
^^^^^^^^^^^^^^^^^^^
  bind to reference   

Aqui, identity conversionde S2é uma subseqüência apropriada Array-to-pointer conversionde S1, portanto, deveria ser melhor do que S1, por que o compilador reclamou g(arr)é uma invocação ambígua?

Eu tenho alguma interpretação errada sobre como classificar as sequências de conversão padrão? Como comparar dois ICS padrão (classificação da conversão contida)?

Respostas

2 jackX Dec 11 2020 at 16:02

O ponto-chave está aqui:

S1 é uma subsequência adequada de S2 (comparando as sequências de conversão na forma canônica definida por [over.ics.scs], excluindo qualquer Lvalue Transformation ; a sequência de conversão de identidade é considerada uma subsequência de qualquer sequência de conversão de não identidade) ou , se não for isso

Isso significa que, para chamada de função g(arr), todas as conversões de array em ponteiro não são usadas para determinar a classificação. Em outras palavras, de tipo int[2]para tipo int*, há apenas uma conversão de identidade que costumava determinar a classificação. Portanto, S1 de void g(int*);e S2 de void g(int (&arr)[2]);são ICS indistinguíveis, portanto, o compilador fornece um erro ambíguo.

Em contraste, as conversões para void f(int*);e void f(int const*);usadas para comparar a classificação são identity conversione qualification conversion, respectivamente.

De acordo com a regra:

a sequência de conversão de identidade é considerada uma subsequência de qualquer sequência de conversão de não identidade

Conseqüentemente, Qualification conversioné considerado como tendo uma classificação pior do que a de identity conversion. Então, void f(int*)venceu a competição.

cigien Dec 11 2020 at 16:14

Você está tentando aplicar over.ics.rank-3.2.1 a esses conjuntos de sobrecarga, mas esta regra não se aplica a fou g.


Dada a chamada f(arr);, ao realizar a resolução de sobrecarga para f, ambas as sobrecargas exigem uma sequência de conversão padrão que consiste em uma conversão de matriz para ponteiro e ambas têm a mesma classificação, que é correspondência exata . O desempatador usado neste caso é over.match.best # over.ics.rank-3.2.5 :

A sequência de conversão padrão S1 é uma sequência de conversão melhor do que a sequência de conversão padrão S2 se

...

S1 e S2 diferem apenas em sua conversão de qualificação ([conv.qual]) e produzem tipos semelhantes T1 e T2, respectivamente, onde T1 pode ser convertido em T2 por uma conversão de qualificação.

Há um exemplo seguindo esta regra que demonstra como a regra funciona.

Para o conjunto de sobrecarga f, T1é int *e T2é int const *e T1pode ser convertido T2por uma conversão de qualificação.


No caso da chamada g(arr);, ao realizar a resolução de sobrecarga, a sobrecarga g(int (&)[2])é classificada como uma correspondência exata , uma vez que a sequência de conversão padrão necessária é uma Nenhuma conversão necessária .

No entanto, a sobrecarga g(int*)também é classificada como uma correspondência exata , uma vez que a sequência de conversão padrão necessária é uma conversão Array-to-Pointer .

fNo entanto, ao contrário de para , não há regra em [over.ics.rank] que elimine a ambigüidade entre as sequências de conversão padrão para ge a chamada falha.