首页 > 解决方案 > 如何根据模板类的基类专门化成员函数

问题描述

我正在尝试根据模板类型专门化模板类的成员函数。特别是我希望有基于多态类型的专业化。我一直在为语法苦苦挣扎。这是我的尝试,显然会产生错误:声明中的两种或多种数据类型doSomething()

class Base {};
class Derived : public Base {};

template<typename T>
class MyClass
{
public:

  void doSomething();

};

template<>
template<typename T>
typename std::enable_if<std::is_base_of<Derived, T>::value>::type
void MyClass<T>::doSomething() 
{
    // Do something with Derived type
}

template<>
template<typename T>
typename std::enable_if<std::is_base_of<Base, T>::value &&
                       !std::is_base_of<Derived, T>::value>::type
void MyClass<T>::doSomething() 
{
    // So something with Base type
}

template<>
template<typename T>
typename std::enable_if<!std::is_base_of<Derived, T>::value>::type
void MyClass<T>::doSomething() 
{
    // Do something with all other types
}

编译给出..

error: two or more data types in declaration of 'doSomething'

顺便说一句,我确实得到了以下编译,但专业化在运行时没有按预期工作。基类型和派生类型最终会通过doSomething().

class Base {};
class Derived : public base {};

template<typename T>
class MyClass
{
public:

  void doSomething()
  {
       // Do something for non-specialized types
  }    
};

template<>
void MyClass<Derived>::doSomething() 
{
    // Do something with Derived type
}

template<>
void MyClass<Base>::doSomething() 
{
    // So something with Base type
}

什么是正确的语法?

标签: c++c++11templatessfinaetemplate-specialization

解决方案


您不能doSomething仅仅因为它不是模板而专门化。MyClass是一个模板,您可以专门化该类,每个专业化都有一个doSomething. 如果这不是您想要的,那么您需要进行doSomething模板重载,并且为了使 SFINAE 工作,必须对doSomething模板参数而不是参数进行 SFINAE 检查MyClass。最后你的支票是错的。

所以这是我的版本:

template<class T> struct MyClass
{
    template <class U = T>
    auto foo() -> std::enable_if_t<std::is_base_of_v<Base, U>
                                   && !std::is_base_of_v<Derived, U>>
    {
        foo_base();
    }

    template <class U = T>
    auto foo() -> std::enable_if_t<std::is_base_of_v<Derived, U>>
    {
        foo_derived();
    }

    template <class U = T>
    auto foo() -> std::enable_if_t<!std::is_base_of_v<Base, U>>
    {
        foo_else();
    }
};

这是一系列测试:

class Base {};
class Derived : public Base {};
class A : Base {};
class B : Derived {};
class X {};
auto test()
{
    MyClass<Base>{}.foo();      // foo_base
    MyClass<Derived>{}.foo();   // foo_derived
    MyClass<A>{}.foo();         // foo_base
    MyClass<B>{}.foo();         // foo_derived
    MyClass<X>{}.foo();         // foo_else
}

当然,我必须提到 C++17 的干净解决方案:

template<class T> struct MyClass
{
    auto foo() 
    {
        if constexpr (std::is_base_of_v<Derived, T>)
            foo_derived();
        else if constexpr (std::is_base_of_v<Base, T>)
            foo_base();
        else
            foo_else();
    }
};

推荐阅读