【问题标题】:Lambda (passing a lambda to a function by reference vs by value)Lambda(通过引用与按值将 lambda 传递给函数)
【发布时间】:2020-07-23 13:57:06
【问题描述】:

当使用 lambda 创建 std::function 时,std::function 在内部制作 lambda 对象的副本。因此,我们调用 fn() 实际上是在我们的 lambda 副本上执行的,而不是实际的 拉姆达。

根据上面的陈述,在 std::function 总是复制 lambda 对象时,在下面的代码中通过 'reference using &' 或通过 'value' 传递 lambda 有什么意义?

换句话说,在下面的代码中,当'invoke'函数的参数通过引用或值传递时,lambda的哪一部分将不会被修改/影响?

#include <iostream>
#include <functional>
 
void invoke(const std::function<void(void)>& fn) // # PASS LAMBDA BY REFERENCE*********************
 
{
    fn();
}
 
int main()
{
    int i{ 0 };
 
    // Increments and prints its local copy of @i.
    auto count{ [i]() mutable {
      std::cout << ++i << '\n';
    } };
 
    invoke(count);
    invoke(count);
    invoke(count);
 
    return 0;
}

#include <iostream>
#include <functional>
 
void invoke(const std::function<void(void)> fn) // # PASS LAMBDA BY VALUE*********************
{
    fn();
}
 
int main()
{
    int i{ 0 };
 
    // Increments and prints its local copy of @i.
    auto count{ [i]() mutable {
      std::cout << ++i << '\n';
    } };
 
    invoke(count);
    invoke(count);
    invoke(count);
 
    return 0;

【问题讨论】:

  • lambda 不是 std::function...
  • 在这两种情况下,您都可以从 lambda 中创建一个临时的 std::function(通过调用)...
  • 那么使用&运算符有什么意义呢? “(通过电话)”是什么意思?
  • 在这种情况下,您最好使用template &lt;typename Func&gt; void invoke(const Func &amp;func);,这样可以避免将lambda 转换为std::function,从而增加开销。
  • 它类似于void foo(const std::string&amp;) vs void foo(std::string) 而你通过const char*

标签: c++ lambda reference


【解决方案1】:

您混合了两件事 - 通过引用传递和强制转换。

通过引用传递

大致是 lambda 的实现方式:

struct Lambda{
    //Capture by value
    Lambda(int i):_i(i){}

    void operator()() const{
        std::cout << ++_i << '\n';
    }
    mutable int _i;
};
//BTW const is somewhat redundant
void invoke1(const Lambda fn) // # PASS LAMBDA BY VALUE
{
    fn();
}
//Const is important, but not yet.
void invoke2(const Lambda& fn) // # PASS LAMBDA BY REFERENCE
{
    fn();
}
 
int main()
{
    int i{ 0 };
 
    // Increments and prints its local copy of @i.
    Lambda count{i};
 
    invoke1(count);//1
    invoke1(count);//1
    invoke1(count);//1

    invoke2(count);//1
    invoke2(count);//2
    invoke2(count);//3
 
    return 0;
}

代码产生您可能想要的输出。 请注意,mutable 的用法是完全不正确的,但我是这样做的,因此代码可以编译。请参阅 link 了解 std::function 是如何做到这一点的。

铸造

正如@Jarod42 所写,lambda 不是std::function,但它可以通过std::function 的构造函数隐式转换为1。 这根本不是std::function 的实现方式,为了允许捕获任何可调用对象,涉及到更多的魔法。但它说明了这一点。

#include <iostream>
#include <functional>
 
struct Lambda{
    //Capture by value
    Lambda(int i):_i(i){}

    void operator()() const{
        std::cout << ++_i << '\n';
    }
    mutable int _i;
};

struct Function{
    //Makes a copy.
    Function(Lambda lambda):_lambda(std::move(lambda)){}

    void operator()() const{
        _lambda();
    }

    Lambda _lambda;
};


void invoke1(const Function fn) // # PASS FUNCTION BY VALUE
{
    fn();
}
//Const is very important.
void invoke2(const Function& fn) // # PASS FUNCTION BY REFERENCE
{
    fn();
}
 
int main()
{
    int i{ 0 };
 
    // Increments and prints its local copy of @i.
    Lambda count{i};
 
    invoke1(count);//1
    invoke1(count);//1
    invoke1(count);//1

    invoke2(count);//1
    invoke2(count);//1
    invoke2(count);//1
    
 
    return 0;
}

所有invoke2 调用仍会打印1,因为每次调用都会创建一个新的Function 对象,该对象会创建自己的count lambda 对象的本地副本。当然,invoke1 调用也发生了同样的情况,但这并没有什么区别,因为无论哪种方式都可以复制。

注意invoke2(const Function&amp;) 中的 const 非常重要,没有它调用会导致编译错误。这是因为const Function&amp; 允许参数绑定到 L 值和 R 值(~temporaries)。

【讨论】:

  • 非常感谢您的时间和解释。我仍然不明白有什么不同。您能否向我介绍任何书籍或资源以了解 std::function 幕后发生的事情?谢谢
  • @sami 这与std::function 几乎无关。我不确定您到底有什么不清楚,不同之处在于按值传递会创建副本,而按引用传递则不会。但是无论您如何传递,类型转换都会创建一个副本。您的问题是您正在传递一个 lambda 函数,但两个函数都接受一个 lambda,只有 std::function 个对象。您可以搜索“std::function 是如何实施的”,但它不会回答您的问题。您需要查找有关参数传递转换的语言规则。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-11
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多