Перегруженная функция не всегда выбирается перед созданием шаблона

Я создал некоторый код, который использует шаблон функции и перегрузку (не специализацию) этой функции. Когда я вызываю функцию с объектом класса, который является производным от параметра в ней, он использует шаблон, что приводит к ошибке компиляции. Я читал http://www.gotw.ca/publications/mill17.htm и у меня сложилось впечатление, что перегруженные функции всегда будут иметь преимущество перед шаблонами. Я создал похожий пример бездействия:

class ITest
{
public:
    virtual void foo()=0;
};
class TheTest:public ITest
{
public:
    virtual void foo()
    {
    }
};
class test
{
public:
    template<typename T>
    void handle(T par)
    {
        par++;
    }    
    void handle(ITest &t)
    {
        t.foo();
    }
};
void TestThem()
{
    test t;
    t.handle(2);
    t.handle(3.0);
    TheTest t2;
    t.handle(t2);
}

Я бы ожидал t.handle(t2) называть перегруженным void handle(ITest &t) поскольку TheTest происходит от ITest, Однако компилятор выбирает шаблон, который генерирует ошибку. Когда я меняю void handle(ITest &t) в void handle(TheTest &t) он компилируется нормально.

Я исправил это, удалив функцию шаблона и перегрузив все используемые типы, но это обременительно, поскольку все они делают одно и то же.

1 ответ

Решение

У меня сложилось впечатление, что перегруженные функции всегда будут иметь преимущество перед шаблонами.

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

В этом случае, хотя они не одинаково хороши. t2 это TheTestпри запуске разрешения перегрузки находит void handle(ITest &t) а также void handle(TheTest par) (Я создал экземпляр шаблона здесь). Так как версия шаблона даст точное совпадение, это лучшая функция и она выбрана.

Способ исправить это - ограничить шаблон работой только для типов, которые не являются производными от ITest, Если вы измените функцию шаблона на

template<typename T, std::enable_if_t<!std::is_base_of_v<ITest, T>, bool> = true>
void handle(T par)
{
    par++;
}  

Тогда он будет вызываться только для типов, которые не являются производными от ITest, Вы можете увидеть, как это работает в этом живом примере.

Другие вопросы по тегам