Думая об этой проблеме, мы еще нашли ответ на этот вопрос. Я нашел другой способ решения той же проблемы:
template <int N, int M, typename D>
struct call_or_recurse;
template <typename ...Types>
struct dispatcher {
template <typename F, typename ...Args>
static void impl(F f, const std::tuple<Types...>& params, Args... args) {
call_or_recurse<sizeof...(Args), sizeof...(Types), dispatcher<Types...> >::call(f, params, args...);
}
};
template <int N, int M, typename D>
struct call_or_recurse {
// recurse again
template <typename F, typename T, typename ...Args>
static void call(F f, const T& t, Args... args) {
D::template impl(f, t, std::get<M-(N+1)>(t), args...);
}
};
template <int N, typename D>
struct call_or_recurse<N,N,D> {
// do the call
template <typename F, typename T, typename ...Args>
static void call(F f, const T&, Args... args) {
f(args...);
}
};
, который требует изменения реализации delayed_dispatch()
на:
void delayed_dispatch() {
dispatcher<Args...>::impl(func, params);
}
Это работает путем рекурсивного преобразования std::tuple
в отдельный пакет параметров.call_or_recurse
необходима как специализация для завершения рекурсии реальным вызовом, который просто распаковывает завершенный пакет параметров.
Я не уверен, что это в любом случае «лучшее» решение, но это другой способдумать и решать это.
В качестве другого альтернативного решения вы можете использовать enable_if
, чтобы сформировать что-то, возможно, более простое, чем мое предыдущее решение:
#include <iostream>
#include <functional>
#include <tuple>
void f(int a, double b, void* c) {
std::cout << a << ":" << b << ":" << c << std::endl;
}
template <typename ...Args>
struct save_it_for_later {
std::tuple<Args...> params;
void (*func)(Args...);
template <typename ...Actual>
typename std::enable_if<sizeof...(Actual) != sizeof...(Args)>::type
delayed_dispatch(Actual&& ...a) {
delayed_dispatch(std::forward<Actual>(a)..., std::get<sizeof...(Actual)>(params));
}
void delayed_dispatch(Args ...args) {
func(args...);
}
};
int main() {
int a=666;
double b = -1.234;
void *c = NULL;
save_it_for_later<int,double,void*> saved = {
std::tuple<int,double,void*>(a,b,c), f};
saved.delayed_dispatch();
}
Первая перегрузка простоберет еще один аргумент из кортежа и помещает его в пакет параметров.Вторая перегрузка принимает соответствующий пакет параметров, а затем выполняет реальный вызов, при этом первая перегрузка отключается в одном-единственном случае, когда вторая будет жизнеспособной.