Как сравнить две стандартные последовательности преобразования, используя ранг содержащихся преобразований

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
}

Рассмотрим приведенный выше код, № 3 выделен f(ptr), но g(arr)дает ambiguousдиагностику.

Правило выбора наилучшей функции определяется как:

Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если

  • S1 является надлежащей подпоследовательностью S2 (сравнение последовательностей преобразования в канонической форме, определенной в [over.ics.scs], исключая любое преобразование Lvalue; последовательность преобразования идентичности считается подпоследовательностью любой последовательности преобразования, отличной от идентичности ) или если не то

Так что взгляните на over.ics.scs # 3

Они используются для ранжирования стандартных последовательностей преобразования. Ранг последовательности преобразования определяется путем рассмотрения ранга каждого преобразования в последовательности и ранга любой ссылочной привязки.

Согласно моему пониманию приведенного выше правила, я могу понять, почему #3перегрузка лучше всего f(ptr), а именно:

Учитывая S1 как (arr => int *):

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

пока задано S2 как (ptr => int const *)

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

Поскольку identity conversionявляется собственной подпоследовательностью Qualification conversions, значит, S1 лучше, чем S2. Итак, №3 выбран по разрешению перегрузки для f(ptr).

Когда я использую аналогичный процесс для определения того, что лучше всего подходит g(arr), я сталкиваюсь с проблемой.

Опять же, учитывая S1 как (arr => int *)

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

пока задано S2 как (arr => int (& arr) [2])

Когда параметр ссылочного типа связывается непосредственно с выражением аргумента, неявная последовательность преобразования является преобразованием идентичности, если только выражение аргумента не имеет тип, который является производным классом типа параметра, и в этом случае последовательность неявного преобразования является производным преобразование в базу

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

Здесь, identity conversionв S2это собственно подпоследовательности Array-to-pointer conversionиз S1, следовательно , она должна быть лучше , чем S1, почему компилятор жаловался g(arr)неоднозначный вызов?

Могу ли я неправильно понять, как ранжировать стандартные последовательности преобразования? Как сравнить две стандартные ICS (ранг заключенной конверсии)?

Ответы

2 jackX Dec 11 2020 at 16:02

Ключевой момент здесь:

S1 является надлежащей подпоследовательностью S2 (сравнение последовательностей преобразования в канонической форме, определенной в [over.ics.scs], исключая любое преобразование Lvalue ; последовательность преобразования идентичности считается подпоследовательностью любой последовательности преобразования, отличной от идентичности) или если не то

Это означает, что для вызова функции g(arr)все преобразования массива в указатель не используются для определения ранга. Другими словами, от типа int[2]к типу int*существует только преобразование идентичности, которое используется для определения ранга. Следовательно, S1 of void g(int*);и S2 of void g(int (&arr)[2]);являются неотличимыми ICS, следовательно, компилятор выдает неоднозначную ошибку.

Напротив, преобразования для void f(int*);и, void f(int const*);используемые для сравнения ранга, равны identity conversionи qualification conversion, соответственно.

По правилу:

последовательность преобразования идентичности считается подпоследовательностью любой неидентификационной последовательности преобразования

Следовательно, Qualification conversionсчитается, что он имеет более низкий ранг, чем у identity conversion. Итак, void f(int*)конкурс выиграл.

cigien Dec 11 2020 at 16:14

Вы пытаетесь применить over.ics.rank-3.2.1 для этих наборов перегрузки, но это правило не распространяется на любой fили g.


Учитывая вызов f(arr);, при выполнении разрешения перегрузки для fобеих перегрузок требуется стандартная последовательность преобразования, состоящая из преобразования массива в указатель , и обе имеют одинаковый ранг, то есть точное совпадение . В этом случае используется функция разрешения выигрыша over.match.best # over.ics.rank-3.2.5 :

Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если

...

S1 и S2 различаются только их квалификационным преобразованием ([conv.qual]) и дают аналогичные типы T1 и T2, соответственно, где T1 может быть преобразован в T2 путем квалификационного преобразования.

Вот пример, следующий за этим правилом, который демонстрирует, как это правило работает.

Для множества перегрузки f, T1это int *и T2есть int const *, и T1может быть преобразован в T2путем преобразования квалификации.


В случае вызова g(arr);при выполнении разрешения перегрузки перегрузка g(int (&)[2])оценивается как точное соответствие , поскольку стандартная необходимая последовательность преобразования не требуется .

Однако перегрузка g(int*)также оценивается как точное соответствие , поскольку необходимая стандартная последовательность преобразования - это преобразование массива в указатель .

fОднако, в отличие от for , в [over.ics.rank] нет правила, которое устраняет неоднозначность между стандартными последовательностями преобразования для gи вызовом с ошибкой.