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

Вот еще пример, в котором приведение спецификаторов влияет на то, какая последовательность будет выбрана:

int extract( void * );

int extract( const void * );

int* pi;

int main() {

extract( pi ); // выбирается extract( void * )

return 0;

}

Здесь для вызова есть две устоявших функции: extract(void*) и extract(const void*). Последовательность преобразований для функции extract(void*) состоит из трансформации l-значения в r-значение для извлечения значения аргумента, сопровождаемого стандартным преобразованием указателя: из указателя на int в указатель на void. Для функции extract(const void*) такая последовательность отличается от первой дополнительным преобразованием спецификаторов для приведения типа результата от указателя на void к указателю на const void. Поскольку последовательности различаются лишь этой трансформацией, то первая выбирается как более подходящая и, следовательно, наилучшей из устоявших будет функция extract(const void*).

Спецификаторы const и volatile влияют также на ранжирование инициализации параметров-ссылок. Если две такие инициализации отличаются только добавлением спецификатора const и volatile, то инициализация без дополнительной спецификации считается лучшей при разрешении перегрузки:

#include vector

void manip( vectorint );

void manip( const vectorint );

vectorint f();

extern vectorint vec;

int main() {

manip( vec ); // выбирается manip( vectorint )

manip( f() ); // выбирается manip( const vectorint )

return 0;

}

В первом вызове инициализация ссылок для вызова любой функции является точным соответствием. Но этот вызов все же не будет неоднозначным. Так как обе инициализации одинаковы во всем, кроме наличия дополнительной спецификации const во втором случае, то инициализация без такой спецификации считается лучше, поэтому перегрузка будет разрешена в пользу устоявшей функции manip(vectorint).

Для второго вызова существует только одна устоявшая функция manip(const vectorint). Поскольку фактический аргумент является временной переменной, содержащей результат, возвращенный f(), то такой аргумент представляет собой r-значение, которое нельзя использовать для инициализации неконстантного формального параметра-ссылки функции manip(vectorint). Поэтому наилучшей является единственная устоявшая manip(const vectorint).

Разумеется, у функций может быть несколько фактических аргументов. Выбор наилучшей из устоявших должен производиться с учетом ранжирования последовательностей преобразований всех аргументов. Рассмотрим пример:

extern int ff( char*, int );

extern int ff( int, int );

int main() {

ff( 0, 'a' ); // ff( int, int )

return 0;

}

Функция ff(), принимающая два аргумента типа int, выбирается в качестве наилучшей из устоявших по следующим причинам:

* ее первый аргумент лучше. 0 дает точное соответствие с формальным параметром типа int, тогда как для установления соответствия с параметром типа char * требуется стандартное преобразование указателя;

* ее второй аргумент имеет тот же ранг. К аргументу 'a' типа char для установления соответствия со вторым формальным параметром любой из двух функций должна быть применена последовательность преобразований, имеющая ранг расширения типа.

Вот еще один пример:

int compute( const int, short );

int compute( int, double );

extern int iobj;

int main() {

compute( iobj, 'c' ); // compute( int, double )

return 0;

}

Обе функции compute( const int, short ) и compute( int, double ) устояли. Вторая выбирается в качестве наилучшей по следующим причинам:

* ее первый аргумент лучше. Инициализация ссылки для первой устоявшей функции хуже потому, что она требует добавления спецификатора const, не нужного для второй функции;

ее второй аргумент имеет тот же ранг. К аргументу 'c' типа char для установления соответствия со вторым формальным параметром любой из двух функций должна быть применена последовательность трансформаций, имеющая ранг стандартного преобразования.

*

9.4.4. Аргументы со значениями по умолчанию

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

extern void ff( int );

extern void ff( long, int = 0 );