以下代码具有几个常规函数重载,并且-现在-如果没有合适的重载,则该模板函数应用作“包罗万象”。
它几乎如我所愿,除了使用派生类(以前以常规重载形式结束)而由功能模板处理之外。
#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)
基类的函数重载真的比函数模板的质量低吗?如果是这样,有没有简便的方法可以更改此设置?
答案 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。尽管很明显,您需要提前知道要限制什么。