功能模板和常规重载

时间:2018-12-30 12:08:34

标签: c++ overload-resolution function-templates

以下代码具有几个常规函数重载,并且-现在-如果没有合适的重载,则该模板函数应用作“包罗万象”。

几乎如我所愿,除了使用派生类(以前以常规重载形式结束)而由功能模板处理之外。

#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 :(得分:3)

这与“质量”无关。这与转换有关,就像其他任何重载一样。要对调用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");  
}

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