Анимация
JavaScript
|
Главная Библионтека templateo void f<int*>( int* ); tempiate<class т> void f( T* ); ... int *p; f( p ); какая функция будет вызвана? Ответ в данном случае - ... третья функция f. Давайте еще раз рассмотрим прел-ставленный код, комментируя его так, как это было сделано в примере 7-2(а), чтобы сравнить и противопоставить эти два примера. template<class Т> (а) такой же обычный первичный void f( т ); шаблон, как и ранее templateo (в) явная специализация - на этот void f<int*>( int* ); раз это явная специализация (а) tempiate<class Т> (б) второй первичный шаблон, void f( Т* ); перегружающий (а) ... int р; f( р ); вызов (6)! Разрешение перегрузки игнорирует специализацию и работает только с базовыми шаблонами функций. Если изложенное удивляет вас - вы не одиноки в своем удивлении. В свое время это удивляло немалое количество экспертов. Ключ к пониманию приведенного кода прост, и он состоит в том, что специализации не перегружают функции. Перегружаться могут только первичные шаблоны (конечно, наряду с обычными нешаблонными функциями). Рассмотрим еще раз вторую часть правил разрешения перегрузки, но на этот раз в них будут особо акцентированы некоторые моменты. • Если такой "привилегированной" функции нет, в качестве прегенлснтов рассматриваются первичные шаблоны функций. То, какой именно первичный шаблон будет выбран, зависит от того обстоятельства, какой из них в наибольшей степени соответствует аргументам функции и является "наиболее специализированным" [...] • Очевидно, что если имеется только один "наиболее специализированный" первичный шаблон функции, то используется именно он. Если первичный шаблон специализирован для используемых типов, то будет использоваться именно эта специализация; в противном случае будет выполнено инстанци-рование первичного шаблона с корректными типами. Компилятор выбирает только первичный шаблон (или нешаблонную функцию, если таковая имеется). И только после того, как первичный шаблон выбран, компилятор начинает анализировать, есть ли подходящая специализация выбранного шаблона, и если находит таковую, то использует ее. > Рекомендация Стоит запомнить, что специализации шаблона функции не участвуют в разрешении перегрузки. Специализация используется только тогда, когда первичный шаблон функции уже выбран, причем на этот выбор не влияет наличие или отсутствие специализации шаблона. Мораль сей басни такова... Если ваша логика подобна моей, то первый ваш вопрос после этого будет таким: "Но ведь я написал конкретную специализацию для случая, когда параметр функции имеет тип int*, и этот int* совершенно точно соответствует типу аргумента в вызове функции - так почему бы не использовать именно эту специализацию?" Увы, здесь вы ошибаетесь Если вы хотите гарантировать вызов вашей функции в случае точного соответствия, вы должны сделать ее обычной нешаблонной функцией, а не специализацией шаблона. Объяснение, почему специализации не участвуют в перегрузке, очень простое. Комитет по стандарту указал, что было бы удивительно, если бы только из-за того, что вы написали специализацию для некоторого шаблона, изменялся выбор используемого шаблона. Это объяснение вкупе с наличием способа, гарантирующего использование нашей версии функции (достаточно сделать ее не специализацией, а обычной функцией), позволяет нам более точно и ясно понять, почему специализации не влияют на выбор шаблонов. > Рекомендация Мораль №1. Если вы хотите настроить первичный шаблон функции так, чтобы при разрешении перегрузки (или всегда в случае точного соответствия типов) использовалась ваша функция, делайте се не специализацией, а обычной нешаблонной функцией. Следствие. Если вы используете перегрузку шаблона функции, избегайте его специализаций. Но что если вы один из тех, кто не только использует, но и пишет шаблоны функций? Можете ли вы найти лучшее решение и заранее избежать этой и других проблем как для себя, так и для ваших пользователей? Да, можете. пример 7-2(в): иллюстрация к морали №2 tempiate<class т> struct Flmpl; tempiate<class T> void f(T t) {Flmpl<T>::f( t );} Руками не трогать! :) tempiate<class T> struct Flmpl { static void f( T t ); Специализируйте здесь > Рекомендация Мораль №2. Если вы пишете первичный шаблон функции, который с большой вероятностью будет специализирован, лучше писать его как отдельный шаблон функции, который никогда не специализируется и не перегружается и будет просто перенаправлять вызов шаблону класса со статической функцией с той же сигнатурой. Таким образом, все пользователи получают возможность специализировать этот класс как полностью, так и частично, никак не влияя при этом на разрешение перегрузки. Резюме Перегрузка шаблонов функций - вполне корректная вещь. Разрешение перегрузки рассматривает все первичные шаблоны функций в качестве равных претендентов, так что здесь вполне применим ваш опыт работы с перегрузкой обычных функций С++. Компилятор рассматривает все видимые шаблоны функций и выбирает из них наиболее подходящий. Специализация шаблонов функций существенно менее интуитивна. Во первых, вы не можете частично их специализировать - вместо этого вы должны использовать перегрузку. Во-вторых, специализации шаблонов функций не перегружаются. Это означает, что любые специализации, написанные вами, не влияют на выбор используемого шаблона, что противоречит интуитивным ожиданиям большинства программистов. В конце концов, если вместо специализации вы напишете обычную функцию с той же сигнатурой, то при разрешении перегрузки будет использована именно она, поскольку обычная функция всегда имеет преимущество перед шаблоном. Если вы пишете шаблон функции, то лучше писать его как шаблон функции, никогда не специализируемый и не перефужаемый, и реализовать посредство.м шаблона класса. Этот своеобразный уровень косвенности позволит вам избежать ограничений и "темных закутков" шаблонов функций, а профаммисты, использующие ваш шаблон, смогут как угодно - полностью ли, частично ли - специализировать шаблон класса, никак НС влияя при этом на работу шаблона функции. Таким образом вы обходите как запрет на частичную специализацию шаблона функции, так и невозможность (иногда неожиданную) перефузкл специализации шаблона функции. Проблема решена. Если вы используете обычный шаблон функции (не реализованный через шаблон класса), то для того, чтобы ваша специализированная версия данной функции принимала участие в перегрузке, вы должны сделать ее не специализацией шаблона, а обычной нешаблонной функцией с той же сигнатурой. 0 1 2 3 4 5 6 7 8 9 10 11 12 [ 13 ] 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |