|
Программирование >> Инициализация объектов класса, структура
template <typename Type> Type sum( Type*, int ); template <typename Type> Type sum( Type, int ); int ia[1024]; Type == int ; sum<int>( int*, int ); Type == int*; sum<int*>( int*, int ); ?? конкретизированы могут быть оба: int ival1 = sum<int>( ia, 1024 ); Как это ни удивительно, такой вызов не приводит к неоднозначности. Шаблон конкретизируется из первого определения, так как выбирается наиболее специализированное определение. Поэтому для аргумента Type принимается int, а не int* . Для того чтобы один шаблон был более специализирован, чем другой, оба они должны иметь одни и те же имя и число параметров, а для параметров разных типов, как, скажем, T* и T в предыдущем примере, параметр в одном шаблоне должен быть способен принять более широкое множество фактических аргументов, чем соответствующий параметр в другом. Например, для шаблона sum(Type*, int) вместо первого формального параметра функции разрешается подставлять только фактические аргументы типа указатель . В то же время в шаблоне sum(Type, int) первому формальному параметру могут соответствовать фактические аргументы любого типа. Первый шаблон sum(Type*, int) допускает более узкое множество аргументов, чем второй, т.е. он более специализирован, а следовательно, он и конкретизируется при вызове функции. 10.8. Разрешение перегрузки при конкретизации A В предыдущем разделе мы видели, что шаблон функции может быть перегружен. Кроме того, допускается использование одного и того же имени для шаблона и обычной шаблон функции template <class Type> Type sum( Type, int ) { /* ... */ } обгчная функция (не шаблон) функции: double sum( double, double ); Когда программа обращается к sum() , вызов разрешается либо в пользу конкретизированного экземпляра шаблона, либо в пользу обычной функции - это зависит от того, какая функция лучше соответствует фактическим аргументам. (Для решения такой проблеме! применяется процесс разрешения перегрузки, описанный в главе 9.) Рассмотрим следующий пример: В некоторых ситуациях неоднозначности при вызове не возникает, хотя по шаблону можно конкретизировать две разных функции. Если имеются следующие два шаблона для функции sum() , то предпочтение будет отдано первому даже тогда, когда void calc( int ii, double dd ) { что будет вызвано: конкретизированн экземпляр шаблона обгчная функция? sum( dd, ii ); Будет ли при обращении к sum(dd,ii) вызвана функция, конкретизированная из шаблона, или обычная функция? Чтобы ответить на этот вопрос, выполним по шагам процедуру разрешения перегрузки. Первый шаг заключается в построении множества функций-кандидатов состоящего из одноименных вызванной функций, объявления которых видны в точке вызова. Если существует шаблон функции и на основе фактических аргументов вызова из него может быть конкретизирована функция, то она будет являться кандидатом. Так ли это на самом деле, зависит от результата процесса вывода аргументов шаблона. (Этот процесс описан в разделе 10.3.) В предыдущем примере для вывода значения аргумента Type шаблона используется фактический аргумент функции dd. Тип выведенного аргумента оказывается равным double, и к множеству функций-кандидатов добавляется функция sum(double, int) . Таким образом, для данного вызова имеются два кандидата: конкретизированная из шаблона функция sum(double, int) и обычная функция sum(double, double) . После того как функции, конкретизированные из шаблона, включены в множество кандидатов, процесс вывода аргументов шаблона продолжается как обычно. Второй шаг процедуры разрешения перегрузки заключается в выборе устоявших функций из множества кандидатов. Напомним, что устоявшей называется функция, для которой существуют преобразования типов, приводящие каждый фактический аргумент функции к типу соответствующего формального параметра. (В разделе 9.3 описаны преобразования типов, применимые к фактическим аргументам функции.) Нужные трансформации существуют как для конкретизированной функции sum(double, int) , так и для обычной функции sum(double, double) . Следовательно, обе они являются устоявшими. Проведем ранжирование преобразований типов, примененных к фактическим аргументам для выбора наилучшей из устоявших функций. В нашем примере оно происходит следующим образом: Для конкретизированной из шаблона функции sum(double, int) : для первого фактического аргумента как сам этот аргумент, так и формальный параметр имеют тип double, т.е. м1 видим точное соответствие; для второго фактического аргумента как сам аргумент, так и формальный параметр имеют тип int, т.е. снова точное соответствие. Для обычной функции sum(double, double) : для первого фактического аргумента как сам этот аргумент, так и формальный параметр имеют тип double - точное соответствие; для второго фактического аргумента сам этот аргумент имеет тип int, а формальный параметр - тип double, т.е. необходимо стандартное преобразование между целым и плавающим типами. шаблон функции template <class T> Предположим, что шаблон функции sum() объявлен следующим образом: int sum( T*, int ) { ... } Для описанного вызова функции вывод аргументов шаблона будет неудачным, так как фактический аргумент типа double не может соответствовать формальному параметру типа T*. Поскольку для данного вызова и данного шаблона конкретизировать функцию невозможно, в множество кандидатов ничего не добавляется, т.е. единственным его элементом останется обычная функция sum(double, double). Именно она вызывается при обращении, и ее второй фактический аргумент приводится к типу double. А если вывод аргументов шаблона завершается удачно, но для них есть явная специализация? Тогда именно она, а не функция, конкретизированная из обобщенного определение шаблона функции template <class Type> Type sum( Type, int ) { /* ... */ } / / явная специазация для Type == double template<> double sum<double>( double,int ); обгчная функция double sum( double, ouble sum( double, double ); le dd ) { пециализация шаблона sum<double>() void manip( int ii, double dd вызается явная спец! sum( dd, ii ); шаблона, попадает в множество кандидатов. Например: При обращении к sum() внутри manip() в процессе вывода аргументов шаблона обнаруживается, что функция sum(double,int) , конкретизированная из обобщенного шаблона, должна быть добавлена к множеству кандидатов. Но для нее имеется явная специализация, которая и становится кандидатом. На более поздних стадиях анализа выясняется, что эта специализация дает наилучшее соответствие фактическим аргументам вызова, так что разрешение перегрузки завершается в ее пользу. Явные специализации шаблона не включаются в множество кандидатов автоматически. Лишь в том случае, когда вывод аргументов завершается успешно, компилятор будет рассматривать явные специализации данного шаблона: Если рассматривать только первый аргумент, то обе функции одинаково хороши. Однако для второго аргумента конкретизированная из шаблона функция лучше. Поэтому наиболее подходящей (лучшей из устоявших) считается функция sum (double, int). Функция, конкретизированная из шаблона, включается в множество кандидатов только тогда, когда процесс вывода аргументов завершается успешно. Неудачное завершение в данном случае не является ошибкой, но кандидатом функция считаться не будет.
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |