【问题标题】:Template specialization and derived classes in C++C++ 中的模板特化和派生类
【发布时间】:2013-02-11 05:09:26
【问题描述】:

我有这个简单的代码:

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);
}

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

specialization
template
template

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

【问题讨论】:

  • 你到底是什么意思?这个问题完全模棱两可
  • @Desolator 他的意思是他希望从 A 以及 A 实例本身派生的任何东西都使用对 A 引用的覆盖而不是通用模板扩展。他调用 foo()BC 实例使用的是泛型,而不是覆盖。
  • @WhozCraig 谢谢!这正是我想要的。 A 派生类有一个我需要调用的函数。其他类型没有这个功能,我需要使用traits来代替。
  • @Felics 你现在应该知道了。那个“专业化”不是。它是一个超载。有一个不同的世界
  • @WhozCraig 是的,你是对的,但在我的情况下,行为是相同的:)

标签: c++ templates


【解决方案1】:

是的,这是可能的,但您必须稍微更改您的代码。

首先,从技术上讲,第二个函数f() 不是模板函数的特化,而是重载。解决重载时,为所有类型不是A 的参数选择模板版本,因为它是完美匹配:T 被推断为等于参数的类型,所以在调用f(b) 时,为例如,在类型推导之后,编译器将不得不在以下两个重载之间进行选择:

void f(B&){printf("template\n");}
void f(A&){printf("specialization\n");}

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

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

您可以将std::enable_ifstd::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; }

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

#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"
}

编辑:

如果您使用的编译器与 C++11 不完全兼容(因此不支持函数模板上的默认模板参数),您可能希望将 f() 的模板重载定义更改为如下:

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

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

【讨论】:

  • +1 给你安迪。我正要开始输入 SFINAE 对此的答案,感谢您为我节省了击键 =)
  • 注意:p 名称在模板参数列表中是可选的。
  • @MatthieuM.:正确。我会删除它
  • @AndyProwl 感谢您的帮助。不幸的是,这在 VS2010 中没有编译:错误 C4519:默认模板参数只允许在类模板上
  • @Felics:我明白了。那是因为 VC10 不完全兼容 C++11。但是,有一种方法可以解决这个问题。我会将其添加到我的答案中。
猜你喜欢
  • 2018-01-22
  • 2010-12-28
  • 1970-01-01
  • 1970-01-01
  • 2016-09-26
  • 2017-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多