变体模板函数接受lambda

时间:2014-07-13 03:16:48

标签: c++ templates c++11 lambda variadic-templates

我试图理解我在下面的代码中遇到的编译器错误。我有一个接受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.

如果我将声明更改为variadic:

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

然后它适用于上面的玩具示例,但对于真正的问题,我需要任意参数。 我在这里缺少什么,或者是另一种方法来实现这个目标吗?

编辑:这被标记为重复不正确,我相信 - 这个骗子没有回答我问的问题。这个问题特别与此处的可变参数模板问题有关:请注意,当我将模板切换为非可变参数时,lambda会正确转换为std :: function类型,如预期的那样。无论参数的数量是多少都是如此,只要不以可变方式处理它。

但是,它确实使用可变参数版本,尽管期望将参数包解包为一组实际参数,并且在函数中显式指定模板参数列表呼叫网站。

2 个答案:

答案 0 :(得分:3)

您的情况下可变参数模板的问题是编译器不知道您明确指定的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));
}

答案 1 :(得分:3)

The question我以前链接过,因为副本会准确解释您的情况。

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

在你的例子中

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

executeWithResultHandler<int>([](int arg){});

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


  

如果我将声明更改为variadic,那么它可以正常工作

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) {}));