可变模板函数接受lambda

Variadic template function accepting lambda

本文关键字:lambda 函数      更新时间:2023-10-16

我试图理解编译器错误,我得到下面的代码。我有一个可变的模板函数,它接受一个lambda使用指定的类型,并且尝试调用该函数将导致模板由于不匹配而不被视为有效的候选模板。

#include <functional>
template<typename ... ResultTypes>
void executeWithResultHandler(std::function<void (ResultTypes...)> lambda)
{
}
int main(int argc, char **argv)
{
    executeWithResultHandler<int>([] (int arg) {
    });
    return 0;
}

这会导致以下错误:

$ c++ -std=c++11 reduction.cpp 
reduction.cpp:10:5: error: no matching function for call to 'executeWithResultHandler'
    executeWithResultHandler<int>([] (int arg) {
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
reduction.cpp:4:6: note: candidate template ignored: could not match 'function<void (int, type-parameter-0-0...)>' against
      '<lambda at reduction.cpp:10:35>'
void executeWithResultHandler(std::function<void (ResultTypes...)> lambda)
     ^
1 error generated.

如果我改变声明为非可变的:

template<typename ResultType>
void executeWithResultHandler(std::function<void (ResultType)> lambda)
{
}

则适用于上面的玩具示例,但对于实际问题,我需要任意参数。我在这里是否遗漏了什么,或者有其他方法可以完成这个任务?

编辑:我认为这篇文章被错误地标记为副本——被复制的人没有回答我的问题。这个问题特别与可变变量模板问题有关:请注意,当我将模板切换为非可变变量时,lambda会像预期的那样正确地转换为std::函数类型。无论参数有多少,只要不是以可变的方式处理,这都是正确的。

然而,尽管期望参数包被解压缩为一组实际参数,并且在函数调用处明确规范了模板参数列表,却不能专门用于可变版本。

在您的情况下,可变模板的问题是编译器不知道您显式指定的int是否为ResultTypes...完整列表,因此它试图从您给它的参数中推断可选的剩余参数,这显然失败了。这是可变模板参数的一个常见缺陷,不仅限于lambdas。

一个解决方案总是暗示你从编译器中拿走这个选项,例如

template<typename ... ResultTypes>
void executeWithResultHandler_impl(std::function<void (ResultTypes...)> lambda)
{
}
template<typename ... ResultTypes, typename F>
void executeWithResultHandler(F&& lambda)
{
    executeWithResultHandler_impl(std::function<void (ResultTypes...)>(lambda));
}

我之前链接的重复问题准确地解释了你的情况。

一个std::function不是一个lambda,它是一种容器类型,可以存储任何类型的可调用的对象。您可以将lambda分配给std::function,但在这种情况下,必要的转换由std::function构造函数执行。

在你的例子中

template<typename ... ResultTypes>
void executeWithResultHandler(std::function<void (ResultTypes...)> lambda)
{}
executeWithResultHandler<int>([](int arg){});

编译器无法从上面的lambda表达式推断参数包ResultTypes中的类型。模板实参推导需要精确匹配,不考虑隐式转换,并且这里涉及的类型,如前所述,是完全不同的。


如果我改变声明为不可变的,那么它就可以工作了

template<typename ResultType>
void executeWithResultHandler(std::function<void (ResultType)> lambda)
{}
 executeWithResultHandler<int>([](int arg){});

这样做是因为不再涉及模板参数推导。executeWithResultHandler只接受一个模板形参,您已经显式地指定了这个形参,并且由于lambda隐式地可转换为std::function,因此重载解析将找到一个匹配的。

请注意,在第一种情况下,除了int之外,可能还有更多的模板参数,您没有显式指定。


通过显式地将lambda转换为std::function,可以使原始示例正常工作。

executeWithResultHandler<int>(std::function<void(int)>([] (int arg) {}));