我如何根据模板 class 的基础 class 专门化成员函数

How do I specialize member functions based on the base class of the templated class

我正在尝试根据模板类型专门化模板的成员函数 class。特别是我想要基于多态类型的专业化。我一直在为语法而苦苦挣扎。这是我的尝试,它显然会产生错误: 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
}

正确的语法是什么?

您不能仅仅因为它不是模板就专门化 doSomethingMyClass 是一个模板,您可以专门化 class,每个专门化有一个 doSomething。如果这不是您想要的,那么您需要进行 doSomething 模板重载,并且为了使 SFINAE 正常工作,SFINAE 检查必须在 doSomething 模板参数上进行,而不是在 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();
    }
};

另一种可能的解决方案通过 ForFoo 模板 class,它定义了 foo() 方法,仅针对 BaseDerived class是的。所以MyClass<T>可以继承自ForFoo<T>.

我的意思是...如果您按如下方式定义 ForFoo 组模板 classes

template <typename T, typename = void>
struct ForFoo
 { void foo () { std::cout << "other type" << std::endl; } };

template <typename T>
struct ForFoo<T,
   typename std::enable_if<std::is_base_of<Base, T>::value
                        && ! std::is_base_of<Derived, T>::value>::type>
 { void foo () { std::cout << "Base type" << std::endl; } };

template <typename T>
struct ForFoo<T,
   typename std::enable_if<std::is_base_of<Derived, T>::value>::type>
 { void foo () { std::cout << "Derived type" << std::endl; } };

MyClass就变成了

template <typename T>
struct MyClass : public ForFoo<T>
 { };

以下是完整的 C++11 示例

#include <iostream>
#include <type_traits>

class Base {};
class Derived : public Base {};
class A : Base {};
class B : Derived {};
class X {};

template <typename T, typename = void>
struct ForFoo
 { void foo () { std::cout << "other type" << std::endl; } };

template <typename T>
struct ForFoo<T,
   typename std::enable_if<std::is_base_of<Base, T>::value
                        && ! std::is_base_of<Derived, T>::value>::type>
 { void foo () { std::cout << "Base type" << std::endl; } };

template <typename T>
struct ForFoo<T,
   typename std::enable_if<std::is_base_of<Derived, T>::value>::type>
 { void foo () { std::cout << "Derived type" << std::endl; } };

template <typename T>
struct MyClass : public ForFoo<T>
 { };

int main ()
 {
   MyClass<Base>{}.foo();      // Base
   MyClass<Derived>{}.foo();   // Derived
   MyClass<A>{}.foo();         // Base
   MyClass<B>{}.foo();         // Derived
   MyClass<X>{}.foo();         // other
 }