Как специализировать функции-члены на основе базового класса шаблонного класса - PullRequest
0 голосов
/ 20 октября 2018

Я пытаюсь специализировать функции-члены класса шаблона в зависимости от типа шаблона.В частности, я хотел бы иметь специализации, основанные на полиморфных типах.Я боролся с синтаксисом.Вот моя попытка, которая, очевидно, приводит к ошибке: два или более типов данных в объявлении 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 голосов
/ 20 октября 2018

Другое возможное решение проходит через шаблонный класс ForFoo, который определяет метод foo(), с парой специализаций только для классов Base и Derived.Поэтому 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
 }
0 голосов
/ 20 октября 2018

Вы не можете специализироваться doSomething просто потому, что это не шаблон.MyClass - это шаблон, и вы можете специализировать класс, каждая специализация имеет один 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();
    }
};
...