|
Программирование >> Инициализация объектов класса, структура
int compute( const int&, sho int compute( int&, double ); ort ); extern int iobj; int main() { compute( iobj, c ); compute( int&, double ) return 0; Вот еще один пример: Обе функции compute( const int&, short ) и compute( int&, double ) устояли. Вторая выбирается в качестве наилучшей по следующим причинам: 1. ее первый аргумент лучше. Инициализация ссылки для первой устоявшей функции хуже потому, что она требует добавления спецификатора const, не нужного для второй функции; 2. ее второй аргумент имеет тот же ранг. К аргументу c типа char для установления соответствия со вторым формальным параметром любой из двух функций должна быть применена последовательность трансформаций, имеющая ранг стандартного преобразования. 9.4.4. Аргументы со значениями по умолчанию Наличие аргументов со значениями по умолчанию способно расширить множество устоявших функций. Устоявшими являются функции, которые вызываются с данным списком фактических аргументов. Но такая функция может иметь больше формальных параметров, чем задано фактических аргументов, в том случае, когда для каждого неуказанного параметра есть некое значение по умолчанию: Функция ff() , принимающая два аргумента типа int, выбирается в качестве наилучшей из устоявших по следующим причинам: 1. ее первый аргумент лучше. 0 дает точное соответствие с формальным параметром типа int, тогда как для установления соответствия с параметром типа char * требуется стандартное преобразование указателя; 2. ее второй аргумент имеет тот же ранг. К аргументу a типа char для установления соответствия со вторым формальным параметром любой из двух функций должна быть применена последовательность преобразований, имеющая ранг расширения типа. extern void ff( int ); extern void ff( long, int = 0 ); int main() { ff( 2L ); соответствует ff( long, 0 ); соответствует ff( long, int ); соответствует ff( int ); ff( 0, 0 ); ff( 0 ); ff( 3.14 ); ошибка: неоднозначность Для первого и третьего вызовов функция ff() является устоявшей, хотя нередан всего один фактический аргумент. Это обусловлено следующими причинами: 1. для второго формального параметра есть значение по умолчанию; 2. первый параметр типа long точно соответствует фактическому аргументу в первом вызове и может быть приведен к типу аргумента в третьем вызове за счет последовательности, имеющей ранг стандартного преобразования. Последний вызов является неоднозначным, поскольку обе устоявших функции могут быть выбраны, если применить стандартное преобразование к первому аргументу. Функции ff(int) не отдается предпочтение только потому, что у нее один параметр. Упражнение 9.9 Объясните, что происходит при разрешении перегрузки для вызова функции compute() внутри main() . Какие функции являются кандидатами? Какие из них устоят после первого шага? Какие последовательности преобразований надо применить к фактическому аргументу, чтобы он соответствовал формальному параметру для каждой namespace primerLib { void compute(); void compute( const void * ); using primerLib::compute; void compute( int ); void compute( double, double = 3.4 ); void compute( char*, char* = 0 ); int main() { compute( 0 ); return 0; устоявшей функции? Какая функция будет наилучшей из устоявших? Что будет, если using-объявление поместить внутрь miain() перед вызовом comipute() ? Ответьте на те же вопросы. 10. Шаблоны функций В этой главе рассказывается, что такое шаблон функции, как его определять и использовать. Это довольно просто, и многие программисты применяют шаблоны, определенные в стандартной библиотеке, даже не понимая, с чем они работают. Только пользователи, хорошо знающие язык С++, самостоятельно определяют и применяют шаблоны функций так, как здесь описано. Поэтому материал данной главы следует рассматривать как переход к более сложным аспектам C++. начнем с рассказа о том, что такое шаблон функции и как его определять, затем на простом примере проиллюстрируем использование шаблонов. Далее мы перейдем к темам, требующим больших знаний. Сначала посмотрим на усложненные примеры применения шаблонов, затем подробно остановимся на выведении (deduction) их аргументов и покажем, как их можно задавать при конкретизации (instantiation) шаблона функции. После этого мы посмотрим, каким образом компилятор конкретизирует шаблоны и какие требования предъявляются в этой связи к организации наших программ, а также обсудим, как определить специализацию для такой конкретизации. Затем в данной главе будут изложены вопросы, представляющие интерес для проектировщиков шаблонов функций. Мы объясним, как можно перегружать шаблоны и как применительно к ним работает разрешение перегрузки. Мы также расскажем о разрешении имен в определениях шаблонов функций и покажем, как можно определять шаблоны в пространствах имен. Глава завершается развернутым примером. 10.1. Определение шаблона функции Иногда может показаться, что сильно типизированный язык создает препятствия для реализации совсем простых функций. Например, хотя следующий алгоритм функции min() тривиален, сильная типизация требует, чтобы его разновидности были int min( int a, int b ) { return a < b ? a : b; double min( double a, double b ) { return a < b ? a : b; реализованы для всех типов, которые мы собираемся сравнивать: Заманчивую альтернативу явному определению каждого экземпляра функции min() представляет использование макросов, расширяемых препроцессором: #define min(a, b) ((a) < (b) ? (a) : (b)) Но этот подход таит в себе потенциальную опасность. Определенный выше макрос правильно работает при простых обращениях к min() , например:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.002
При копировании материалов приветствуются ссылки. |