如何基于模板化类的基类来专门化成员函数

时间:2018-10-20 00:38:56

标签: c++ c++11 templates sfinae template-specialization

我试图根据模板的类型来专门化模板类的成员函数。我特别希望基于多态类型的专业化。我一直在努力的语法。这是我的尝试,显然会产生错误: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
}

正确的语法是什么?

2 个答案:

答案 0 :(得分:3)

不能仅仅因为doSomething不是模板来专门化MyClassdoSomething是模板,您可以对类进行特殊化,每个特殊化都有一个doSomething。如果这不是您想要的,则需要使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(); } }; 上进行SFINAE检查参数。最后,您的支票错了。

这是我的版本:

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

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

iframe

答案 1 :(得分:1)

另一种可能的解决方案是通过ForFoo模板类,该模板类定义了foo()方法,并且仅对BaseDerived类有两个特殊化。因此MyClass<T>可以从ForFoo<T>继承。

我的意思是……如果您按照以下方式定义ForFoo组模板类

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
 }