Шаблон функции и регулярные перегрузки - PullRequest
0 голосов
/ 30 декабря 2018

В следующем коде есть пара регулярных перегрузок функций, и - теперь - функция шаблона, предназначенная как универсальная, если перегрузка не подходит.

Это почти работает как яхочу, за исключением того, что использование производных классов (которые ранее заканчивались обычной перегрузкой) вместо этого обрабатывается шаблоном функции.

#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)

Действительно ли перегрузка функции базового класса оценивается как "более низкое качество", чем шаблон функции?Если так, есть ли простой способ изменить это?

https://ideone.com/jD2lgz

1 Ответ

0 голосов
/ 30 декабря 2018

Дело не в "качестве".Речь идет о преобразованиях, как и при любой другой перегрузке.Чтобы выполнить разрешение перегрузки при вызове 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");  
}

, который даст точный результат, который вы ожидали .Хотя очевидно, что вам нужно заранее знать, что ограничивать.

...