函数模板适用于本地lambda,但不适用于其他函数

时间:2016-05-07 16:36:04

标签: c++ templates lambda c++14

所以我写了一个函数,它组成了“顺序”的void lambdas,这样我就可以在算法中一次性使用它们:

template <typename F, typename... Fs>
auto lambdaList(F f, Fs... fs)
{
    return [=] (auto&... args) { f(args...); lambdaList(fs...)(args...); };
}

template <typename F>
auto lambdaList(F f)
{
    return [=] (auto&... args) { f(args...); };
}

如果我使用本地lambda,它可以工作,但是当我在不同的命名空间中使用函数时,它不起作用:

#include <iostream>

namespace foo {
    void a() { std::cout << "a\n"; }
    void b() { std::cout << "b\n"; }
}

template <typename F, typename... Fs>
auto lambdaList(F f, Fs... fs)
{
    return [=] (auto&... args) { f(args...); lambdaList(fs...)(args...); };
}

template <typename F>
auto lambdaList(F f)
{
    return [=] (auto&... args) { f(args...); };
}

int main() {
    auto printStarBefore = [] (const std::string& str) { 
        std::cout << "* " + str; 
    };
    auto printStarAfter = [] (const std::string& str) { 
        std::cout << str + " *" << std::endl; 
    };    

    lambdaList(printStarBefore, printStarAfter)("hi");  // ok
    lambdaList(foo::a, foo::b)();                       // error
}

错误为no matching function for call to 'lambdaList()',其中包含:

main.cpp:11:56: note:   candidate expects at least 1 argument, 0 provided
     return [=] (auto&... args) { f(args...); lambdaList(fs...)(args...); };
                                              ~~~~~~~~~~^~~~~~~

为什么它有时会起作用,但有时却不起作用?

1 个答案:

答案 0 :(得分:6)

您需要反转您的功能:

template <typename F>
auto lambdaList(F f)
{
    return [=] (auto&... args) { f(args...); };
}

template <typename F, typename... Fs>
auto lambdaList(F f, Fs... fs)
{
    return [=] (auto&... args) { f(args...); lambdaList(fs...)(args...); };
}

按原样,在递归情况下,无法通过非限定查找找到基本情况 - 只能通过参数依赖查找找到它。如果参数与lambdaList不在同一名称空间中,则根本不会找到它,并且递归步骤将始终调用自身。这是你错误的根源。

新排序允许通过正常的非限定查找找到基本情况lambdaList() - 现在它在递归lambdaList()的定义点处可见。

那就是说,我们可以做得更好。编写一个调用所有内容的函数:

template <typename... Fs>
auto lambdaList(Fs... fs) { 
    using swallow = int [];
    return [=](auto const&... args) {
        (void)swallow{0,
            (void(fs(args...)), 0)...
        };
    };
}

现在我们不需要担心任何类型的查找。如果您可以访问支持某些C ++ 1z功能的现代编译器,则可以通过以下方式大大减少上述内容:

template <typename... Fs>
auto lambdaList(Fs... fs) { 
    return [=](auto const&... args) {
        (fs(args...), ...);
    };
}

这是完全可以理解的!