В сегодняшней краткой заметке я опишу тонкий момент связанный с перегрузкой и специализацией функций. Не так давно встретилось на практике и появился повод проапдейтить запись в личной БД на эту тему. Этой информацией и поделюсь.
Пример
template<typename T> void foo(T); // 1
template<> void foo(int*); // 2
template<typename T> void foo(T*); // 3
template<typename T> void bar(T); // 4
template<typename T> void bar(T*); // 5
template<> void bar(int*); // 6
void f() {
int i;
foo(&i);
bar(&i);
}
Итак у нас есть 2 абсолютно одинаковых набора функций, однако, как вы понимаете, без подвоха тут не обойдется. Какие именно из них будут вызваны?
Объяснение
Те кто ответил 3 и 6 могут сегодня потратить на торчание на хабре на полчаса больше рабочего времени чем обычно. Чтобы объяснить такое поведение нужно вспомнить какие существуют категории функций с точки зрения перегрузки.
1. Обычные функции;
2. базовые шаблоны. Это функции вида
template<typename T> void bar(T);
3. Специализации шаблонов функций.
Поведение компилятора для разрешения перегрузки между этими тремя категориями вполне ожидаемо. Сначала выбирается лучший кандидат среди обычных функций и базовых шаблонов, причем предпочтение отдается обычным функциям. Если же более подходящим является базовый шаблон, то проверяется а нет ли у него еще более подходящих специализаций и, если есть, выбирается одна из них.
Тонкий же момент заключается в том, что для того чтобы специализация шаблонной функции считалась таковой, базовый шаблон должен быть объявлен в коде перед этой специализацией.
То есть в примере выше (2) является специализацией не (3) а (1), поэтому для перегрузки будет выбран более подходящий базовый шаблон (3). Во втором же случае (6) является специализацией (5), которая в свою очередь является лучшей кандидатурой, чем (4).
Автор: rpz