Шаблон функции и регулярные перегрузки
Следующий код имеет несколько регулярных перегрузок функций и - теперь - шаблонную функцию, предназначенную как универсальную, если перегрузка не подходит.
Это почти работает так, как я хочу, за исключением того, что использование производных классов (которые ранее заканчивались обычной перегрузкой) вместо этого обрабатывается шаблоном функции.
#include <iostream>
class Base { };
class AnotherBase { };
class Derv : public Base{ };
class Derv2 : public Base { };
class DervDerv : public Derv { };
void f(const Base &b)
{
printf("b(Base)\n");
}
void f(const Derv &b)
{
printf("b(Derv)\n");
}
template<class T> void f(const T& t)
{
printf("b(template)\n");
}
int main() {
f(Base());
f(AnotherBase());
f(Derv());
f(Derv2());
f(DervDerv());
return 0;
}
Таким образом, вывод, который я получаю, это...
b(Base)
b(template)
b(Derv)
b(template)
b(template)
... когда я наивно ожидал этого:
b(Base)
b(template)
b(Derv)
b(Base)
b(Derv)
Действительно ли перегрузка функции базового класса оценивается как "более низкое качество", чем шаблон функции? Если так, есть ли простой способ изменить это?
1 ответ
Дело не в "качестве". Речь идет о преобразованиях, как и при любой другой перегрузке. Чтобы сделать разрешение перегрузки на вызов f(Derv2());
компилятор будет синтезировать объявление, подобное этому, из вашего шаблона функции:
void f(const Derv2& t);
Который это противопоставляет другим заявленным перегрузкам. Эта перегрузка выбирается по той же причине f(const Derv &)
лучше, чем f(const Base &)
когда ты пишешь f(Derv());
,
Шаблон "поймай все" сделает именно это, он поймает все, для чего нет точной пользовательской перегрузки. Если вы хотите предотвратить это, вам нужно ограничить шаблон метапрограммированием. Простой трюк, основанный на SFINAE, будет выглядеть так:
template<class T>
auto f(const T& t) -> std::enable_if_t<!std::is_convertible<T*, Base*>::value>
{
printf("b(template)\n");
}
Это дает точный результат, который вы ожидали. Хотя очевидно, что вам нужно заранее знать, что ограничивать.