Выбрать главу

точное соответствие. Тип фактического аргумента точно соответствует типу формального параметра. Например, если в множестве перегруженных функций print() есть такие:

void print( unsigned int );

void print( const char* );

void print( char );

*

то каждый из следующих трех вызовов дает точное соответствие:

unsigned int a;

print( 'a' ); // соответствует print( char );

print( "a" ); // соответствует print( const char* );

print( a ); // соответствует print( unsigned int );

соответствие с преобразованием типа. Тип фактического аргумента не соответствует типу формального параметра, но может быть преобразован в него:

void ff( char );

ff( 0 ); // аргумент типа int приводится к типу char

*

* отсутствие соответствия. Тип фактического аргумента не может быть приведен к типу формального параметра в объявлении функции, поскольку необходимого преобразования не существует. Для каждого из следующих двух вызовов функции print() соответствия нет:

// функции print() объявлены так же, как и выше

int *ip;

class SmallInt { /* ... */ };

SmallInt si;

print( ip ); // ошибка: нет соответствия

print( si ); // ошибка: нет соответствия

*

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

* преобразование l-значения в r-значение;

* преобразование массива в указатель;

* преобразование функции в указатель;

преобразования спецификаторов.

*

(Подробнее они рассмотрены ниже.)Категория соответствия с преобразованием типа является наиболее сложной. Необходимо рассмотреть несколько видов такого приведения: расширение типов (promotions), стандартные преобразования и определенные пользователем преобразования. (Расширения типов и стандартные преобразования изучаются в этой главе. Определенные пользователем преобразования будут представлены позднее, после детального рассмотрения классов; они выполняются конвертером, функцией-членом, которая позволяет определить в классе собственный набор “стандартных” трансформаций. В главе 15 мы познакомимся с такими конвертерами и с тем, как они влияют на разрешение перегрузки функций.)

При выборе лучшей из устоявших функций для данного вызова компилятор ищет функцию, для которой применяемые к фактическим аргументам преобразования являются “наилучшими”. Преобразования типов ранжируются следующим образом: точное соответствие лучше расширения типа, расширение типа лучше стандартного преобразования, а оно, в свою очередь, лучше определенного пользователем преобразования. Мы еще вернемся к ранжированию в разделе 9.4, а пока на простых примерах покажем, как оно помогает выбрать наиболее подходящую функцию.

9.3.1. Подробнее о точном соответствии

Самый простой случай возникает тогда, когда типы фактических аргументов совпадают с типами формальных параметров. Например, есть две показанные ниже перегруженные функции max(). Тогда каждый из вызовов max() точно соответствует одному из объявлений:

int max( int, int );

double max( double, double );

int i1;

void calc( double d1 ) {

max( 56, i1 ); // точно соответствует max( int, int );

max( d1, 66.9 ); // точно соответствует max( double, double );

}

Перечислимый тип точно соответствует только определенным в нем элементам перечисления, а также объектам, которые объявлены как принадлежащие к этому типу:

enum Tokens { INLINE = 128; VIRTUAL = 129; };

Tokens curTok = INLINE;

enum Stat { Fail, Pass };

extern void ff( Tokens );

extern void ff( Stat );

extern void ff( int );

int main() {

ff( Pass ); // точно соответствует ff( Stat )

ff( 0 ); // точно соответствует ff( int )

ff( curTok ); // точно соответствует ff( Tokens )

// ...

}

Выше уже упоминалось, что фактический аргумент может точно соответствовать формальному параметру, даже если для приведения их типов необходимо некоторое тривиальное преобразование, первое из которых – преобразование l-значения в r-значение. Под l-значением понимается объект, удовлетворяющий следующим условиям:

* можно получить адрес объекта;