Как сравнить две стандартные последовательности преобразования, используя ранг содержащихся преобразований
#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 (ранг заключенной конверсии)?
Ответы
Ключевой момент здесь:
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*)
конкурс выиграл.
Вы пытаетесь применить 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
и вызовом с ошибкой.