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

hg_*_*git 17 c++ lambda function c++11 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);
}
Run Code Online (Sandbox Code Playgroud)

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

template<typename Functor>
    void f(Functor functor)

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

template<typename Functor>
    void f(Functor &&functor)
Run Code Online (Sandbox Code Playgroud)

什么是更好的方式和原因?这些都有限制吗?

sky*_*ack 8

作为一个可能的缺点,请注意,如果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);
}
Run Code Online (Sandbox Code Playgroud)

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

  • @NathanOliver当然,你不能再重复使用lambda了.如果要将其传递给示例中的两个函数,则无法实现. (3认同)

Kam*_*rek 5

由于 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;
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句,如果您想通过性能来判断它实际上没有区别,lambda 非常小且易于复制。

此外 - 如果您想将 lambda 作为参数(而不是包含它的变量)传递,您应该使用转发引用,以便它可以工作。

  • “由于 lambda 表达式可以有自己的字段”和“lambda 非常小且易于复制”是两个相互矛盾的陈述。lambda 的大小取决于这些字段的大小。可以创建一个任意大且很难复制的 lambda。 (3认同)