传递lambda作为参数 - 通过引用或值?

时间:2017-02-21 16:43:32

标签: c++ function c++11 lambda c++14

我编写了一个模板代码,它以仿函数作为参数,经过一些处理后,执行它。虽然其他人可能会将该函数传递给lambda,函数指针甚至是std::function,但它主要用于lambda(不是我禁止其他格式)。我想问一下,我应该怎样拿这个lambda - 按价值?引用?或其他什么。

示例代码 -

#include <iostream>
#include <functional>
using namespace std;

template<typename Functor>
void f(Functor functor)
{
    functor();
}

void g()
{
    cout << "Calling from Function\n";
}

int main() 
{
    int n = 5;

    f([](){cout << "Calling from Temp Lambda\n";});

    f([&](){cout << "Calling from Capturing Temp Lambda\n"; ++n;});

    auto l = [](){cout << "Calling from stored Lambda\n";};
    f(l);

    std::function<void()> funcSTD = []() { cout << "Calling from std::Function\n"; };
    f(funcSTD);

    f(g);
}

在上面的代码中,我可以选择将其中任何一个 -

template<typename Functor>
    void f(Functor functor)

template<typename Functor>
    void f(Functor &functor)

template<typename Functor>
    void f(Functor &&functor)

更好的方法是什么?为什么?这些都有任何限制吗?

2 个答案:

答案 0 :(得分:7)

作为一个可能的缺点,请注意,如果lambda不可复制,则通过副本传递不起作用。如果你可以逃脱它,通过副本就可以了 举个例子:

#include<memory>
#include<utility>

template<typename F>
void g(F &&f) {
    std::forward<F>(f)();
}

template<typename F>
void h(F f) {
    f();
}

int main() {
    auto lambda = [foo=std::make_unique<int>()](){};

    g(lambda);
    //h(lambda);
}

在上面的代码段中,由于lambdafoo无法复制。由于std::unique_ptr的复制构造函数被删除,因此删除了其复制构造函数 另一方面,F &&f接受左值和左值引用作为转发引用,以及const引用。
换句话说,如果你想多次重复使用相同的lambda作为参数,你就不能通过复制得到你的对象而你必须移动它,因为它不可复制(嗯,实际上你可以,这是一个将它包装在lambda中的问题,通过引用捕获外部的一个。)

答案 1 :(得分:4)

由于lambda表达式可以有自己的字段(如类),复制/使用引用可能会导致不同的结果。这是一个简单的例子:

template<class func_t>
size_t call_copy(func_t func) {
    return func(1);
}

template<class func_t>
size_t call_ref(func_t& func) {
    return func(1);
}

int main() {
    auto lambda = [a = size_t{0u}] (int val) mutable {
        return (a += val);
    };
    lambda(5);

    // call_ref(lambda); – uncomment to change result from 5 to 6
    call_copy(lambda);

    std::cout << lambda(0) << std::endl;

    return 0;
}

顺便说一下,如果你想通过表现来判断它,实际上没有区别,lambda非常小,很容易复制。

另外 - 如果你想传递lambda作为参数(而不是包含它的变量),你应该使用转发引用,这样它才能工作。