You could use something like this to create a tuple of all of the argument types, and then pass a default constructed instance of it to std::apply
. The specialisation list would need to be quite long though to cover all of the const
, volatile
, noexcept
, and ref-qualified variants though, and of course it cannot work with template or overloaded functions.
Eg:
template <typename T>
struct arg_extractor : arg_extractor<decltype(&T::operator())> {
};
template <typename R, typename... Args>
struct arg_extractor<R (*)(Args...)> {
using type = std::tuple<R, Args...>;
};
template <typename R, typename C, typename... Args>
struct arg_extractor<R (C::*)(Args...)> {
using type = std::tuple<R, Args...>;
};
template <typename R, typename C, typename... Args>
struct arg_extractor<R (C::*)(Args...) const> {
using type = std::tuple<R, Args...>;
};
template <typename R, typename C, typename... Args>
struct arg_extractor<R (C::*)(Args...) noexcept> {
using type = std::tuple<R, Args...>;
};
template <typename R, typename C, typename... Args>
struct arg_extractor<R (C::*)(Args...) const noexcept> {
using type = std::tuple<R, Args...>;
};
// All the rest...
template <typename T>
using arg_extractor_t = typename arg_extractor<T>::type;