Наследование шаблонных методов
У меня есть класс, похожий на следующее:
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, на который ссылаются в других ответах, довольно крутой, хотя он не соответствовал моим потребностям (необходимость указать шаблон в параметре также не будет для меня полезной, особенно во время выполнения).