我有以下模板功能:
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
可以是除函数指针之外的任何类型,如何解决这个问题。
答案 0 :(得分:8)
从14.1p11开始:
不得遵循功能模板的模板参数包 除非可以从parameter-type-list推导出该模板参数,否则使用另一个模板参数 函数模板或具有默认参数(14.8.2)。
如果要将callable保留为最后一个参数,可以使用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)...));
}
答案 1 :(得分:4)
写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 {}
这很傻。如果您对传入类型的要求更复杂(例如,您希望使用参数调用它),则必须使用更高级的traits类。
然后您使用以下内容扩充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 */ }
这是非常迟钝的。
答案 2 :(得分:0)
如果你想将一包参数作为模板,你不能用你已经做过的方式写这个:
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; });