Наследование шаблонных методов

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

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 ответа

Решение

Я думаю, что ответ @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.

Вы сделали.

Я предполагаю, что вы после 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);
};

Что может быть полезным в этом случае - это использование функции макроса в классе-обёртке, 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, на который ссылаются в других ответах, довольно крутой, хотя он не соответствовал моим потребностям (необходимость указать шаблон в параметре также не будет для меня полезной, особенно во время выполнения).

Другие вопросы по тегам