在模板实例化之前并不总是选择重载函数

Overloaded function is not always selected before template instantiation

我创建了一些使用函数模板和该函数的重载(不是特化)的代码。当我使用派生自参数的 class 对象调用该函数时,它使用模板导致编译错误。我一直在阅读 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) 时,它可以正常编译。

我通过删除模板函数并为所有使用的类型重载来修复它,但这很麻烦,因为它们都做完全相同的事情。

I had the impression that overloaded functions would always have preference above templates.

这是真的,但前提是非模板函数和模板函数同样好。在这种情况下,使用非模板函数。

在这种情况下,尽管它们并不同样好。 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 的类型调用。您可以在 live example.

中看到它的工作