Одним из решений, которое я вижу, является добавление другого параметра шаблона для подписи метода и передача его явно при специализации.
Это на правильном пути, но вам не нужно передаватьэто явно;Вы можете извлечь тип из базового класса:
template<class Base, class... Arg>
class CommonBaseImpl : public Base {
public:
// how do I overload Base::foo here?
void foo(Arg... arg) override {
commonCode(std::forward<Arg>(arg)...);
}
protected:
// how do I define commonCode with Base::foo signature below?
void commonCode(Arg... arg) { ... }
};
template <class Base, class Foo = decltype(&Base::foo)>
struct BaseSelector;
template <class Base, class... Arg>
struct BaseSelector<Base, void (Base::*)(Arg...)>
{
using type = CommonBaseImpl<Base, Arg...>;
};
template <class Base>
using CommonBase = typename BaseSelector<Base>::type;
[Живой пример]
Это работает с использованием частичной специализации шаблона класса для декомпозиции типа функции.Параметр шаблона Foo
из BaseSelector
будет содержать тип указателя на элемент foo
.Чтобы получить этот тип, мы используем decltype(&Base::foo)
, аргумент по умолчанию для этого параметра.
Однако нам необходимо получить доступ к отдельным типам аргументов из этого типа.Обычно это делается с использованием частичной специализации шаблона, как здесь.По сути, основной шаблон говорит: «Этот шаблон класса принимает два типа, Base
и Foo
».Они типы, и мы больше ничего о них не знаем.Мы также не используем их ни для чего (основной шаблон даже не определен).
Затем мы предоставляем специализацию.Это фактически говорит: «Когда тип Foo
оказывается указателем на функцию-член Base
, которая возвращает void
и принимает аргументы типа Arg...
, то сделайте это: {частично специализированное определение класса}».На практике это просто способ присвоения имен различным компонентам типа указатель на член.