У меня есть следующая функция шаблона:
template <typename...Args, typename Func>
void call(const char *name, Args...args, Func f)
{
f(3);
}
Когда я пытаюсь использовать его, как
call("test", 1, 2, 3, [=](int i) { std::cout<< i; });
Компилятор жалуется, что не может вывести аргумент шаблона Func
,
Как решить эту проблему, зная, что args
может быть любого типа, кроме указателя на функцию.
Написать get_last
, который извлекает последний элемент пакета параметров.
Назови это f
, Вызов f
,
В качестве примера,
template<typename T0>
auto get_last( T0&& t0 )->decltype(std::forward<T0>(t0))
{
return std::forward<T0>(t0);
}
template<typename T0, typename... Ts>
auto get_last( T0&& t0, Ts&&... ts )->decltype(get_last(std::forward<Ts>(ts)...))
{
return get_last(std::forward<Ts>(ts)...);
}
если вас не волнует разрешение перегрузки, просто позвоните get_last
и рассматривать его как функтор может быть достаточно:
template <typename...Args>
void call(const char *name, Args...&& args)
{
auto&& f = get_last(std::forward<Args>(args)...);
f(3);
}
Следующим шагом будет сделать SFINAE enable_if
магия, чтобы сделать call
не соответствует, если вы не передали действительный функтор в последнюю очередь: однако, это, вероятно, излишним.
Чтобы обнаружить, если f(3)
будет работать, простой черт класс:
// trivial traits class:
template<typename T>
struct is_type:std::true_type {};
template<typename Functor, typename=void>
struct can_be_called_with_3:std::false_type {}
template<typename Functor>
struct can_be_called_with_3<Functor,
typename std::enable_if<
std::is_type< decltype(
std::declval<Functor>(3)
) >::value
>::type
>:std::true_type {}
что довольно глупо Класс «черт характера» должен быть использован, если ваши требования к переданному типу являются более сложными (скажем, вы хотите, чтобы он вызывался с аргументами).
Тогда вы увеличиваете call
с:
template <typename...Args>
auto call(const char *name, Args...&& args)
-> typename std::enable_if<
can_be_called_with_3< decltype( get_last(std::forward<Args>(args)... ) ) >::value
>::type
{ /* body unchanged */ }
что довольно тупо
От 14.1п11:
Пакет параметров шаблона шаблона функции не должен соблюдаться
другим параметром шаблона, если этот параметр шаблона не может быть выведен из списка параметров типа
шаблона функции или имеет аргумент по умолчанию (14.8.2).
Если вы хотите оставить вызываемый объект последним аргументом, вы можете использовать forward_as_tuple
:
template <typename...Args, typename Func>
void call(const char *name, std::tuple<Args...> args, Func f)
{
f(3);
}
call("test", std::forward_as_tuple(1, 2, 3), [=](int i) { std::cout<< i; });
Мы можем на самом деле сделать лучше, синтезируя tuple
также содержать вызываемое:
#include <tuple>
template<typename... Args_F>
void call_impl(const char *name, std::tuple<Args_F... &&> args_f) {
auto &&f = std::get<sizeof...(Args_F) - 1>(args_f);
f(3);
}
template<typename...ArgsF>
void call(const char *name, ArgsF &&...args_f) {
call_impl(name, std::tuple<ArgsF &&...>(std::forward<ArgsF>(args_f)...));
}
если вы хотите добавить пакет аргументов в качестве шаблона, вы не можете написать это так, как вы это уже делали:
template <typename...Args, typename Func>
void call(const char *name, Args...args, Func f)
{
f(3);
}
Но вы можете упаковать их в std::tuple
:
template <typename...Args, typename Func>
void call(const char *name, std::tuple<Args...> args, Func f)
{
f(3);
}
call("test", std::forward_as_tuple(1, 2, 3), [=](int i) { std::cout<< i; });