Частичная специализация шаблона для указателя шаблона на функцию
Рассмотрим следующий реферат Subscription
учебный класс:
template <typename TMessage>
class Subscription {
public:
virtual ~Subscription() {}
virtual bool handle(const TMessage &) = 0;
};
В некоторых случаях было бы удобно, если бы один класс мог реализовать этот абстрактный класс несколько раз - даже для одного и того же TMessage
- а не насильственное наследование.
Чтобы достичь этого, я использую указатель шаблона для работы следующим образом:
template <typename TMessage, typename TCaller, bool(TCaller::*TMethod)(const TMessage &)>
class Invoker : public Subscription<TMessage> {
public:
Invoker(TCaller *caller) :
m_caller(*caller) {
}
virtual bool handle(const TMessage &message) {
return (m_caller.*TMethod)(message);
}
protected:
TCaller &m_caller;
};
Это позволяет создавать сгенерированные шаблоном реализации абстрактного класса следующим образом:
(вместо реализации в нескольких разных классах и разделения объектов между ними):
struct MyMessage {};
class Logic {
public:
Logic() :
m_subscription1(new Invoker<MyMessage, Logic, &Logic::handleSubscription1>(this)),
m_subscription2(new Invoker<MyMessage, Logic, &Logic::handleSubscription2>(this)) {
}
~Logic() {
delete m_subscription1;
delete m_subscription2;
}
bool handleSubscription1(const MyMessage &message) {
// handle message... uses class members
return true;
}
bool handleSubscription2(const MyMessage &message) {
// handle message... uses class members
return false;
}
private:
Subscription<MyMessage> *m_subscription1;
Subscription<MyMessage> *m_subscription2;
};
Дело в том, что я хотел бы разрешить пользовательские модули (т.е. Logic
класс) реализация void
функция ручки, а также bool
вариант.
Теперь, конечно, я мог бы создать 2 разных Invoker
классы - один реализован так же, как в приведенном выше коде, а другой принимает третий аргумент шаблона функции, возвращающей пустоту: void(TCaller::*TMethod)(const TMessage &)
, В функции handle функции возврата void я бы вызвал указанную функцию и вернул true.
Но мне было интересно, есть ли способ использовать тот же Invoker
имя класса, так что пользователю не нужно будет соответствовать право Invoker
к возвращаемому значению его функции дескриптора. Вроде как перегрузка функций - но для шаблонных классов.
Конечно, если бы я просто создал два класса с одним и тем же именем (каждый из которых принимает разные аргументы шаблона функции возвращаемого типа), компилятор потребовал бы переопределения параметра шаблона.
Поэтому я попытался добиться этого с частичной специализацией шаблона, но не смог найти способ определить третий аргумент шаблона, не получив ошибку компиляции - я даже не уверен, что это правильный путь.
Когда я попробовал это:
template <typename TMessage, typename TCaller, typename TMethod>
class Invoker : public Subscription<TMessage> {
};
template <typename TMessage, typename TCaller, bool(TCaller::*TMethod)(const TMessage &)>
class Invoker<TMessage, TCaller, TMethod> {
public:
Invoker(TCaller *caller) :
m_caller(*caller) {
}
virtual bool handle(const TMessage &message) {
return (m_caller.*TMethod)(message);
}
protected:
TCaller &m_caller;
};
template <typename TMessage, typename TCaller, void(TCaller::*TMethod)(const TMessage &)>
class Invoker<TMessage, TCaller, TMethod> {
public:
Invoker(TCaller *caller) :
m_caller(*caller) {
}
virtual bool handle(const TMessage &message) {
(m_caller.*TMethod)(message);
return true;
}
protected:
TCaller &m_caller;
};
Я получил следующие ошибки компиляции в обеих реализациях Invoker:
ошибка: несоответствие типа / значения в аргументе 3 в списке параметров шаблона для класса шаблона Invoker
ошибка: ожидал тип, получил TMethod
И следующие ошибки компиляции в классе логики (для каждого Invoker):
В конструкторе Logic::Logic():
ошибка: несоответствие типа / значения в аргументе 3 в списке параметров шаблона для класса шаблона Invoker
ошибка: ожидал тип, получил &Logic::handleSubscription1
ошибка: неверное преобразование из логики * const в int
ошибка: невозможно преобразовать int* в подписку * при инициализации
Что я делаю не так и возможно ли это с частичной специализацией шаблона? Если есть какое-либо возможное решение, я хотел бы знать, возможно ли это в C++11, а также в C++98.
Благодарю.
1 ответ
Вы можете сделать
template <typename TMethod, TMethod method> class Invoker;
// partial specialization for TMethod = bool(TCaller::*)(const TMessage &)
template <typename TMessage, typename TCaller, bool(TCaller::*method)(const TMessage &)>
class Invoker<bool(TCaller::*)(const TMessage &), method> :
public Subscription<TMessage>
{
public:
explicit Invoker(TCaller& caller) : m_caller(caller) {}
virtual bool handle(const TMessage &message) {
return (m_caller.*method)(message);
}
protected:
TCaller &m_caller;
};
// partial specialization for TMethod = void(TCaller::*)(const TMessage &)
template <typename TMessage, typename TCaller, void(TCaller::*method)(const TMessage &)>
class Invoker<void(TCaller::*)(const TMessage &), method> :
public Subscription<TMessage>
{
public:
explicit Invoker(TCaller& caller) : m_caller(*caller) {}
virtual bool handle(const TMessage &message) {
(m_caller.*method)(message);
return true;
}
protected:
TCaller &m_caller;
};
И использование это что-то вроде:
class Logic {
public:
Logic() :
m_subscription1(std::make_unique<Invoker<decltype(&Logic::handleSubscription1), &Logic::handleSubscription1>>(*this)),
m_subscription2(std::make_unique<Invoker<decltype(&Logic::handleSubscription2), &Logic::handleSubscription2>>(*this)) {
}
bool handleSubscription1(const MyMessage &message) {
// handle message... uses class members
return true;
}
void handleSubscription2(const MyMessage &message) {}
private:
std::unique_ptr<Subscription<MyMessage>> m_subscription1;
std::unique_ptr<Subscription<MyMessage>> m_subscription2;
};