Наследование шаблонных методов - PullRequest
8 голосов
/ 15 июля 2011

У меня есть класс, подобный следующему:

class SomeClass
{
    public:
        template<typename... Args>
        void doSomething(Args && ... args);

        //... other methods etc.
};

Однако, что я действительно хочу, так это иметь два разных типа SomeClass.В идеале я мог бы получить из общего интерфейса SomeOtherClass, но мне нужно иметь другую реализацию doSomething, и шаблонные методы не могут быть виртуальными.Я мог бы создать шаблонный класс, но тогда каждый метод, который принимает один из них (а их много), сам должен быть шаблоном и т. Д.

Лучшее, что я смог придумать, - это реализоватьоба типа doSomething в базовом классе и этот метод вызывают виртуальный метод, чтобы определить, какой использовать во время выполнения.

Есть ли лучшее решение?

Дальнейшее объяснение

У меня есть много методов, которые выглядят примерно так:

void foo(SomeClass * obj);

foo вызывает obj->doSomething, и все это прекрасно работает, однако с тех пор я понял, что мне нужен другой тип SomeClassно хотите, чтобы он работал с этими же методами, например:

class SomeClass
{
    public:
        // This won't work
        template<typename... Args>
        virtual void doSomething(Args && ... args) = 0;

        // ... other common methods
};

class TheFirstType
{
    public:
        template<typename... Args>
        void doSomething(Args && ... args);

        // ... other specific methods
};

class TheSecondType
{
    public:
        template<typename... Args>
        void doSomething(Args && ... args);

        // ... other specific methods
};

Выше было бы идеально, если бы это было законно, но виртуальные методы не могли быть шаблонизированы.До сих пор я обходил это ограничение, определяя только doSomething в базовом классе, но с реализацией для TheFirstType и TheSecondType, отделенными оператором if, который проверяет, какой тип на самом деле является экземпляром:

template<typename... Args>
void SomeClass::doSomething(Args && ... args)
{
    if (this->type() == FIRST_TYPE) {
        // ... the implementation that should rightfully be part of TheFirstType
    } else if (this->type() == SECOND_TYPE) {
        // ... the implementation that should be part of TheSecondType
    }
}

Это кажется грязным, поэтому мне было интересно, есть ли лучший способ.

Ответы [ 3 ]

6 голосов
/ 16 июля 2011

Я думаю, что ответ @ stijn правильный;у вас есть идеальный случай для CRTP .Вы можете изменить свою логику в соответствии с этим.

template<class T>
class SomeClass
{
public:
  template<typename... Args>
  void doSomething(Args && ... args)
  {
    static_cast<T*>(this)->doSomething(...);
  }
  //other method can be virtual
  virtual void foo ()
  {
    doSomething(...);
    // ... other code;
  }
};

Теперь просто наследуйте эти class другим дочерним классам:

class TheFirstType : SomeClass<TheFirstType>
{
public:
  template<typename... Args>
  void doSomething(Args && ... args) { ... }

  virtual void foo ()
  {
  }
};   // do same for TheSecondType.

Вы закончили.

2 голосов
/ 15 июля 2011

я предполагаю, что вы после CRTP (хотя я не уверен, как iammilind указывает в комментарии):

template< class Parent >
class SomeClassBase : public Parent
{
public:
  //common methods go here
};

class SomeClass : public SomeClassBase< SomeClass >
{
public:
  template<typename... Args>
  void doSomething(Args && ... args);
};

class SomeOtherClass : public SomeClassBase< SomeOtherClass >
{
public:
  template<typename... Args>
  void doSomething(Args && ... args);
};
0 голосов
/ 01 июня 2018

Что может быть полезно в этом случае, это использование макро-функции в классе-обёртке, SomeClass.Преимуществом макросов является подстановка текста.Обычно мы не можем передать функцию со всеми ее параметрами как объект в C ++ (по крайней мере, до C ++ 11, за исключением этого, я не уверен).Однако с помощью макросов мы можем передавать практически любой тип текста.Дополнительное преимущество заключается в том, что он удалит повторяющийся код if / else для каждой функции, имеющей этот шаблонно-виртуальный конфликт.

Мы можем настроить макрос следующим образом:

#define CLASS_SELECT(func) \
do { \
    if (this->type() == FIRST_TYPE) { \
        theFirstType.func; \
    } else if (this->type() == SECOND_TYPE) { \
        theSecondType.func; \
    } \
} while (0)

Если вы не знакомы с инкапсуляцией do while, посмотрите здесь .Класс будет использовать макрос как:

class SomeClass
{
    TheFirstType theFirstType;
    TheSecondType theSecondType;
public:
    template<typename... Args>
    void doSomething(Args && ... args)
    {
        CLASS_SELECT(doSomething (&args...) );
    }
};

В приведенном выше примере я создал специализированные объекты внутри SomeClass.Я не уверен, что именно такой дизайн вы бы предпочли, и если бы не было, я бы рассмотрел использование статических функций получения и т. П. В макросе.

В любом случае, я признаю, что мое решение может небыть идеальным, но это лучшее, что у меня есть (у меня та же проблема, что и у вас в моем коде).Я признаю, что CRTP, на который ссылаются в других ответах, довольно крутой, хотя он не соответствовал моим потребностям (необходимость указывать шаблон в параметре для меня тоже не сработает, особенно во время выполнения).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...