C++中的模板特化和派生类

Mir*_*pas 14 c++ templates

我有这个简单的代码:

class A{};
class B : public A{};
class C : public B{};

class Test
{
    public:
        template<typename T>
        void f(T&){printf("template\n");}
        void f(A&){printf("specialization\n");}
};

int main()
{
    A a;
    B b;
    C c;

    Test test;
    test.f(a);
    test.f(b);
    test.f(c);
}
Run Code Online (Sandbox Code Playgroud)

当我运行它(VS2010)时,我有这个输出:

specialization
template
template
Run Code Online (Sandbox Code Playgroud)

是否可以使用A-derived类进行调用以使用特化?

And*_*owl 20

是的,这是可能的,但你必须稍微改变你的代码.

首先,是技术,第二个功能f()没有模板功能的专业化,而是一个过载.当解析重载时,为所有类型不是的参数选择模板版本A,因为它是完全匹配:T推断为等于参数的类型,因此在调用时f(b),例如,在类型推导之后,编译器将具有在以下两个重载之间进行选择:

void f(B&){printf("template\n");}
void f(A&){printf("specialization\n");}
Run Code Online (Sandbox Code Playgroud)

当然,第一个是更好的匹配.

现在,如果您希望在使用作为子类的参数调用函数时选择第二个版本,则A必须使用一些SFINAE技术来防止在T推断类型被推断为类的子类时正确实例化函数模板A.

您可以std::enable_if结合使用std::is_base_of类型特征来实现这一目标.

// This will get instantiated only for those T which are not derived from A
template<typename T,
    typename enable_if<
        !is_base_of<A, T>::value
        >::type* = nullptr
    >
void f(T&) { cout << "template" << endl; }
Run Code Online (Sandbox Code Playgroud)

以下是在完整程序中使用它的方法:

#include <type_traits>
#include <iostream>

using namespace std;

class A{};
class B : public A{};
class C : public B{};
class D {};

class Test
{
    public:

        template<typename T,
            typename enable_if<!is_base_of<A, T>::value>::type* = nullptr
            >
        void f(T&) { cout << ("template\n"); }

        void f(A&){ cout << ("non-template\n");}

};

int main()
{
    A a;
    B b;
    C c;
    D d;
    float f;

    Test test;
    test.f(a); // Will print "non-template"
    test.f(b); // Will print "non-template"
    test.f(c); // Will print "non-template"
    test.f(d); // Will print "template"
    test.f(f); // Will print "template"
}
Run Code Online (Sandbox Code Playgroud)

编辑:

如果您使用的编译器不完全符合C++ 11(因此不支持函数模板上的默认模板参数),您可能需要更改模板重载的定义,f()如下所示:

template<typename T>
typename enable_if<!is_base_of<A, T>::value, void>::type 
f(T&) { cout << ("template\n"); }
Run Code Online (Sandbox Code Playgroud)

程序的行为将是相同的.请注意,如果返回类型f()void,则可以省略enable_if类模板的第二个参数.