【问题标题】:Variadic template function accepting lambdaVariadic模板功能接受Lambda
【发布时间】:2014-07-13 03:16:47
【问题描述】:

我正在尝试了解我在下面的代码中遇到的编译器错误。我有一个接受 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)
{
}

那么它适用于上面的玩具示例,但对于真正的问题,我需要任意参数。 有什么我在这里遗漏的东西,或者另一种方法来完成这个吗?

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

但是,它确实适用于可变参数版本,尽管期望参数包被解包为一组实际参数,并且函数中模板参数列表的显式规范呼叫站点。

【问题讨论】:

  • lambda 不是 std::function 对象。它们只是调用operator() 的函数对象。因此,即使两个 lambda 具有相同的参数类型和返回类型,它们也有两种不同的类型。在此处查看我的相关问题:stackoverflow.com/questions/11628765/…
  • 在一个相关问题中,我有一个包装函数stackoverflow.com/a/21000981/683218,它可以使 std::function 对象在明确的情况下接受 lambda。

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


【解决方案1】:

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

解决方案总是意味着您从编译器中删除此选项,例如

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

【讨论】:

    【解决方案2】:

    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 中的类型。模板参数推导需要完全匹配,不考虑隐式转换,这里涉及到的类型,如前所述,完全不同。


    如果我将声明更改为不可变参数,那么它可以工作

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

    【讨论】:

    • 啊哈。我缺少的是这一点:“请注意,在第一种情况下,除了 int 之外,可能还有更多模板参数,您没有明确指定。”谢谢!
    猜你喜欢
    • 2020-06-26
    • 1970-01-01
    • 2018-06-18
    • 1970-01-01
    • 2014-08-05
    • 1970-01-01
    • 2019-12-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多