void putValues(const int *);
int main() {
putValues(arr); // необходимо 2 преобразования
// массив в указатель + преобразование спецификатора
return 0;
}
для приведения аргумента arr от типа “массив из трех int” к типу “указатель на const int” применяется последовательность преобразований:
* Преобразование массива в указатель, которое трансформирует массив из трех int в указатель на int.
* Преобразование спецификатора, которое трансформирует указатель на int в указатель на const int.
Поэтому было бы более правильно говорить, что для приведения фактического аргумента к типу формального параметра устоявшей функции требуется последовательность преобразований. Поскольку применяется не одна, а несколько трансформаций, то на третьем шаге процесса разрешения перегрузки функции на самом деле ранжируются последовательности преобразований.
Рангом такой последовательности считается ранг самой плохой из входящих в нее трансформаций. Как объяснялось в разделе 9.2, преобразования типов ранжируются следующим образом: точное соответствие лучше расширения типа, а расширение типа лучше стандартного преобразования. В предыдущем примере оба изменения имеют ранг точного соответствия. Поэтому и у всей последовательности такой же ранг.
Такая совокупность состоит из нескольких преобразований, применяемых в указанном порядке:
преобразование l-значения -
расширение типа или стандартное преобразование -
преобразование спецификаторов
Термин преобразование l-значения относится к первым трем трансформациям из категории точных соответствий, рассмотренных в разделе 9.2: преобразование l-значения в r-значение, преобразование массива в указатель и преобразование функции в указатель. Последовательность трансформаций состоит из нуля или одного преобразования l-значения, за которым следует нуль или одно расширение типа или стандартное преобразование, и наконец нуль или одно преобразование спецификаторов. Для приведения фактического аргумента к типу формального параметра может быть применено только одна трансформация каждого вида.
Описанная последовательность называется последовательностью стандартных преобразований. Существует также последовательность определенных пользователем преобразований, которая связана с функцией-конвертером, являющейся членом класса. (Конвертеры и последовательности определенных пользователем преобразований рассматриваются в главе 15.)
Каковы последовательности изменений фактических аргументов в следующем примере?
namespace libs_R_us {
int max( int, int );
double max( double, double );
}
// using-объявление
using libs_R_us::max;
void func()
{
char c1, c2;
max( c1, c2 ); // вызывается libs_R_us::max( int, int )
}
Аргументы в вызове функции max() имеют тип char. Последовательность преобразований аргументов при вызове функции libs_R_us::max(int,int) следующая:
1a. Так как аргументы передаются по значению, то с помощью преобразования l-значения в r-значение извлекаются значения аргументов c1 и c2.
2a. С помощью расширения типа аргументы трансформируются из char в int.
Последовательность преобразований аргументов при вызове функции libs_R_us::max(double,double) следующая:
1b. С помощью преобразования l-значения в r-значение извлекаются значения аргументов c1 и c2.
2b. Стандартное преобразование между целым и плавающим типом приводит аргументы от типа char к типу double.
Ранг первой последовательности – расширение типа (самое худшее из примененных изменений), тогда как ранг второй – стандартное преобразование. Так как расширение типа лучше, чем преобразование, то в качестве наилучшей из устоявших для данного вызова выбирается функция libs_R_us::max(int,int).
Если ранжирование последовательностей преобразований аргументов не может выявить единственной устоявшей функции, то вызов считается неоднозначным. В данном примере для обоих вызовов calc() требуется такая последовательность:
* Преобразование l-значения в r-значение для извлечения значений аргументов i и j.
* Стандартное преобразование для приведения типов фактических аргументов к типам соответствующих формальных параметров.
Поскольку нельзя сказать, какая из этих последовательностей лучше другой, вызов неоднозначен:
int i, j;
extern long calc( long, long );
extern double calc( double, double );
void jj() {
// ошибка: неоднозначность, нет наилучшего соответствия
calc( i, j );
}