简体   繁体   English

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

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

I'm trying to specialize member functions of a template class based on the type of template. 我试图根据模板的类型来专门化模板类的成员函数。 In particular I'd like to have specializations based on polymorphic types. 我特别希望基于多态类型的专业化。 I've been struggling with the syntax. 我一直在努力的语法。 Here is my try which obviously produces the 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();

};

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
}

Compilation gives.. 编译给出

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

BTW, I did get the following to compile, but the specialization did not work as expected at runtime. 顺便说一句,我确实得到了以下内容进行编译,但是专业化在运行时没有按预期工作。 Base and derived types end up going through the non-specialized version 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
}

What would be the correct syntax? 正确的语法是什么?

You cannot specialize doSomething simply because it's not a template. 您不能仅仅因为doSomething不是模板而专门研究它。 MyClass is a template and you can specialize the class, each specialization having one doSomething . MyClass是一个模板,您可以对类进行专门化,每个专门化都有一个doSomething If that's not what you want then you need to make doSomething template overloads and, for the SFINAE to work, the SFINAE check must be done on the doSomething template parameter, not on the MyClass parameter. 如果这不是您想要的,则需要使doSomething模板重载,并且要使SFINAE正常工作,必须在doSomething模板参数而不是MyClass参数上执行SFINAE检查。 Lastly your checks are wrong. 最后,您的支票错了。

So here is my version: 所以这是我的版本:

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

And here is a battery of tests: 这是一系列测试:

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
}

And of course I must mention the C++17 clean solution: 当然,我必须提到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();
    }
};

Another possible solution pass through a ForFoo template class, that define a foo() method, with a couple of specializations for Base only and Derived classes. 另一个可能的解决方案是通过ForFoo模板类,该模板类定义foo()方法,并且对仅Base类和Derived类具有两个特殊化。 So MyClass<T> can inherit from ForFoo<T> . 因此MyClass<T>可以从ForFoo<T>继承。

I mean... if you define a ForFoo set of template classes as follows 我的意思是...如果您按以下方式定义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 simply become MyClass变成

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

The following is a full working C++11 example 以下是完整的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
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM