将函数指针传递给Variadic模板函数

时间:2019-04-07 00:58:52

标签: c++ templates

我有一个函数printint,它使用整数作为参数。我如何将其传递给可变参数模板,如下所示?我有尝试run_callback(printint,1),但它说“没有实例与功能匹配”。请帮忙,谢谢!

void printint(int i)
{

}

template <typename ...Args>
void run_callback(const std::function<void(Args...)>& func, Args ...as)
{
....
}

1 个答案:

答案 0 :(得分:4)

例如,调用函数模板时

run_callback(printint, 1);

编译器将尝试推导模板参数,将其替换为函数参数类型后,将使函数参数类型与参数类型匹配。这里的问题是,没有Args...可以放入const std::function<void(Args...)>&中以使该类型与printint(即void(int))的类型匹配。因此,模板参数推导失败。

如果您只想让run_callback使用具有与Args...匹配的签名的任何函数,请使其引用具有这种签名的函数:

template <typename... Args>
void run_callback(void (&func)(Args...), Args... as)
{
}

live example

但是,这将变得有些脆弱,因为它基本上要求Args中的类型与回调的参数类型完全匹配。最有可能的是,您希望run_callback使用可以用给定的args调用的任何函数(或更一般地说,是任何可调用的)。实现此目的的一种方法是让函数模板接受任何类型的回调,但仅在func参数实际上是可调用的并且使用相应的参数列表可调用时才启用重载:

template <typename F, typename... Args>
auto run_callback(F&& f, Args&&... as) -> std::enable_if_t<std::is_invocable_v<F, Args...>>
{
    f(std::forward<Args>(as)...);  // call the callback
}

live example

最后,如果由于某种原因,您确实确实需要将func参数设置为std::function,则可以将Args...中的func参数包隐藏在non-deduced context

template <typename... Args>
void run_callback(const std::common_type_t<std::function<void(Args...)>>& func, Args... as)
{
}

live example

这里的窍门是使用std::common_type_t<T>,它实际上只是std::common_type<T>::type的简写,最后将再次变成T(注意:没有任何东西关于std::common_type的特殊之处在于它已经可供我们使用;我们可以使用任何其他帮助程序模板;我们所需要的只是将其参数转发给相关名称的东西。通常,要明确推断出您必须插入哪个T才能使C<T>::xyz成为某种类型,这是不可能的。甚至不能保证每个C<T>::xyz都有这样的xyz,或者不能保证T会是一种类型。因此,在 qualified-id 中的嵌套名称说明符被定义为非推论上下文[temp.deduct.type]/5.1。长话短说,这意味着C<T>::xyz现在以参数Args...的类型出现,以使编译器不会尝试从传递给参数的参数中推断出func应该是什么。 Args...参数(即,它不会尝试执行导致原始代码失败的操作)。但是,它仍然可以从func函数参数包中推断出Args...。因此,类型推导将使as成为传递给Args...的参数的类型并且是成功的。成功进行类型推导后,将推导的as...替换回其余参数中,而Args...的类型将变为func,但是现在采用const std::function<void(Args...)>&的类型从Args...参数包中推导出来。

另一种基本相同的方法是,例如,将第一个参数的参数包装在初始化列表中:

as

live example

作为初始化程序列表的参数在这种情况下,[temp.deduct.type]/5.6也使该参数成为非推论上下文,因此,为什么这样做的解释与前面的示例基本相同。请注意,虽然前一种方法从模板声明中解决了问题,但该方法在函数调用的位置解决了该问题。