Lambda(通过引用与值将 lambda 传递给函数)

Lambda (passing a lambda to a function by reference vs by value)

提问人:Sami 提问时间:7/23/2020 最后编辑:Sami 更新时间:7/23/2020 访问量:751

问:

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

根据上面的陈述,在下面的代码中通过“引用 &”或通过“value”传递 lambda 有什么意义,而 std::function 总是复制 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;
C++ Lambda 参考

评论

3赞 Jarod42 7/23/2020
lambda 不是......std::function
1赞 Jarod42 7/23/2020
在这两种情况下,您都可以从 lambda 创建临时(通过调用)...std::function
0赞 Sami 7/23/2020
那么使用&运算符的意义何在呢?“(通过电话)”是什么意思?
1赞 Mirco De Zorzi 7/23/2020
在这种情况下,您最好使用 这将避免将 转换为 ,从而增加开销。template <typename Func> void invoke(const Func &func);lambdastd::function
0赞 Jarod42 7/23/2020
它类似于 vs 而你通过void foo(const std::string&)void foo(std::string)const char*

答:

1赞 Quimby 7/23/2020 #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;
}

该代码生成您可能想要的输出。 请注意,用法是完全不正确的,但我这样做是为了编译代码。请参阅链接了解如何做到这一点。mutablestd::function

铸造

正如@Jarod42所写的,lambda 不是一个 ,但它可以通过 的构造函数隐式转换为 1。 这根本不是实现方式,为了允许捕获任何可调用对象,还涉及更多的魔法。但它说明了这一点。std::functionstd::functionstd::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;
}

所有调用仍会打印,因为在每次调用时都会创建一个新对象,该对象会创建自己的 lambda 对象的本地副本。当然,电话也发生了同样的情况,但这没有区别,因为无论哪种方式都会进行复制。invoke21Functioncountinvoke1

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

评论

0赞 Sami 7/26/2020
非常感谢您的时间和解释。我仍然不明白有什么区别。你能给我介绍一些书籍或资源来了解 std::function 的幕后发生了什么吗?谢谢
0赞 Quimby 7/26/2020
@sami 这几乎与.我不确定你到底不清楚什么,区别在于按值传递会创建一个副本,而通过引用传递不会。 但是,无论您如何传递,类型转换都会创建一个副本。您的问题是您正在传递一个 lambda 函数,但两个函数都不接受 lamba,只接受对象。您可以搜索“如何实现”,但它不会回答您的问题。您需要查找有关参数传递强制转换的语言规则。std::functionstd::functionstd::function