函数模板和常规重载

Function template and regular overloads

下面的代码有几个常规函数重载,现在还有一个模板函数,如果没有合适的重载,它可以作为万能的。

almost 可以按我想要的方式工作,除了使用派生的 classes(之前以常规重载结束)由函数模板处理。

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

基类 class 的函数重载真的比函数模板排名 "lower quality" 吗?如果是这样,有没有简单的方法来改变它?

https://ideone.com/jD2lgz

这与 "quality" 无关。它是关于转换的,就像任何其他重载一样。要对 f(Derv2()); 的调用进行重载解析,编译器将从您的函数模板中合成如下声明:

void f(const Derv2& t);

它与其他声明的重载相对立。出于完全相同的原因选择此重载 f(const Derv &) 是比 f(const Base &) 更好的匹配,当您编写 f(Derv());.

"catch-all" 模板就可以做到这一点,它会捕获所有没有确切的用户定义重载的内容。如果你想防止这种情况,你需要用元编程来约束模板。一个依赖 SFINAE 的简单技巧如下所示:

template<class T>
auto f(const T& t) -> std::enable_if_t<!std::is_convertible<T*, Base*>::value>
{
  printf("b(template)\n");  
}

即产生 exact output you expected。虽然很明显,您需要提前知道要限制什么。