【问题标题】:passing lambda as argument - by reference or value?将 lambda 作为参数传递 - 通过引用或值?
【发布时间】:2017-07-11 10:04:12
【问题描述】:

我编写了一个模板代码,它接受一个函子作为参数,经过一些处理后,执行它。尽管其他人可能会将该函数传递给 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)

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

【问题讨论】:

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


【解决方案1】:

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

在上面的 sn-p 中,lambda 不可复制,因为 foo。由于 std::unique_ptr 的复制构造函数被删除,它的复制构造函数被删除。
另一方面,F &amp;&amp;f 接受左值和右值引用,因为它是转发引用以及 const 引用。
换句话说,如果您想多次重用相同的 lambda 作为参数,如果您的函数通过复制获取对象并且您必须移动它,因为它不可复制(好吧,实际上您可以,这只是将它包装在一个通过引用捕获外部的 lambda 中)。

【讨论】:

  • @NathanOliver 当然,但你不能再重用 lambda。如果你想将它传递给示例中的两个函数,这是不可能的。
  • @skypjack 后续问题 - 除了定义两个类似的重载之外,是否有可能在这样的场景中回退到可移动函子..
  • @AbhinavGauniyal 通过定义两个执行完全相同工作的重载,只能不使用转发引用?我没有得到确切的好处。我是不是误会了什么?
  • @skypjack 我的意思是有两个h() 的定义,其中第一个是值,第二个是&amp;&amp;.. 这样编译就不会失败。
  • @Nik-Lz 不是最好的定义,但接近现实。 ;-)
【解决方案2】:

由于 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 作为参数(不是包含它的变量)传递,你应该使用转发引用以便它可以工作。

【讨论】:

  • “由于 lambda 表达式可以有自己的字段”和“lambda 非常小且易于复制”是两个相互矛盾的陈述。 lambda 有多大取决于这些字段有多大。可以创建任意大且非常难以复制的 lambda。
猜你喜欢
  • 1970-01-01
  • 2012-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-13
  • 2020-03-08
相关资源
最近更新 更多