Я создал структуру типажей FnOnArg, чтобы узнать, может ли Fn вызвать Arg.
template<typename Fn, typename Arg, typename=void>
struct FnOnArg: std::false_type {};
template<typename Fn, typename Arg>
struct FnOnArg<Fn, Arg, std::void_t<decltype(std::declval<Fn>()(std::declval<Arg>()))>>: std::true_type {};
Может работать только на Fn с одним аргументом
Я хочу создать FnOnArgs лайк
template<typename Fn, typename... Args, typename=void>
struct FnOnArgs;
но аргумент шаблона с переменным числом аргументов и аргумент по умолчанию должны быть последним аргументом
Есть ли обходной путь для достижения FnOnArgs?
В C++20 вы можете использовать requires и концепцию





Вы перемещаете фиктивный аргумент шаблона typename = void, чтобы он был где-то раньше typename... Args.
Это означает, что у него больше не может быть аргумента по умолчанию (= void), поэтому вы удаляете его и передаете void явно.
Затем вы создаете еще один шаблон, который оборачивает этот и автоматически добавляет void, поэтому вам не придется передавать его каждый раз.
template <typename Void, typename Fn, typename ...Args>
struct FnOnArgsLow : std::false_type {};
template <typename Fn, typename ...Args>
struct FnOnArgsLow<std::void_t<decltype(std::declval<Fn>()(std::declval<Args>()...))>, Fn, Args...> : std::true_type {};
// The wrapper can be a variable or a class, whatever you like more.
template <typename Fn, typename ...Args>
static constexpr bool FnOnArgs = FnOnArgsLow<void, Fn, Args...>::value;
Невозможно иметь параметр шаблона по умолчанию после пакета параметров.
Возможным решением может быть использование структуры-обертки, которая позволяет упаковывать вариативное количество параметров и затем передавать их как один аргумент. В данном случае для простоты использован std::tuple.
Пример:
namespace detail {
template<typename, typename, typename = void>
struct FnOnArg
: std::false_type {};
template<typename Fn, typename ...Args>
struct FnOnArg<Fn, std::tuple<Args...>, std::void_t<decltype(std::declval<Fn>()(std::declval<Args...>()))>>
: std::true_type {};
}
template <typename Fn, typename ...Args>
struct FnOnArg
: detail::FnOnArg<Fn, std::tuple<Args...>> {};
Использование кортежа для этого является излишним. Меня немного беспокоят затраты времени на компиляцию. По крайней мере, я бы создал свой собственный класс списка типов.
Просто измените расположение параметров шаблона, как показано ниже:
template<typename Fn, typename=void, typename... Arg>
struct FnOnArg: std::false_type {};
template<typename Fn, typename... Arg>
struct FnOnArg<Fn, std::void_t<decltype(std::declval<Fn>()(std::declval<Arg>()...))>, Arg...>: std::true_type {};
//making an alias
template<typename Fn, typename... Args>
constexpr static bool FnOnArg_v = FnOnArg<Fn, void, Args...>::value;
@HolyBlackCat Обновлено.
Почему бы просто не использовать std::is_invocable?