std::functions 和 lambda 函数传递
std::functions and lambda function passing
我有一个 class,它将 std::function
作为参数,我分配了一个 lambda 函数。它在构造函数中工作,但之后停止工作。调试器在第一行 运行 之后说 f
是 "empty"。为什么?
#include <iostream>
#include <string>
#include <functional>
typedef std::function<void(std::string)> const& fn;
class TestClass
{
public:
TestClass(fn _f) : f(_f) { F(); }
void F() { f("hello"); };
private:
fn f;
};
int main()
{
TestClass t([](std::string str) {std::cout << str << std::endl; });
t.F();
return 0;
}
调用t.F()
导致错误。为什么?
我改成下面这样就可以解决:
int main()
{
fn __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();
return 0;
}
但同样,当我将 fn
更改为 auto
时,这不起作用!
int main()
{
auto __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();
return 0;
}
发生这种情况的原因是什么?
typedef std::function<void(std::string)> const& fn;
这不是 std::function
,它是对 std::function
的引用。
TestClass(fn _f) : f(_f) { F(); }
fn f;
在这里你把一个 const&
绑定到一个 std::function
并将它绑定到另一个 const&
到一个 std::function
。构造函数主体中的 F()
起作用,因为至少只要构造函数有效,引用就有效。
TestClass t([](std::string str) {std::cout << str << std::endl; });
这将创建一个从 lambda 创建的 std::function
临时文件。这个临时的持续时间与当前行一样长(直到 ;
)。
然后临时std::function
被丢弃
由于 TestClass
比 const&
多了 std::function
,它不会延长临时对象的生命周期。
所以在该行之后,对 std::function const&
的任何调用都是未定义的行为,您会在稍后对 .F()
的调用中看到这一点。
fn __f = [](std::string str) {std::cout << str << std::endl; };
这确实涉及延长寿命。从 lambda 创建的临时 std::function
的生命周期延长到 __f
变量的生命周期。
顺便说一句,此行还使您的程序格式错误,不需要诊断,因为它有一个包含双下划线的变量。这些标识符是为编译器的实现保留的,您不能创建它们。
TestClass t(__f);
然后我们传递这个引用(指的是延长生命周期的临时),一切正常。
auto __f = [](std::string str) {std::cout << str << std::endl; };
这将创建一个变量 __f
(见上文,错误名称),它是一个 lambda。
lambda 不是 std::function
。 std::function
可以从 lambda 隐式创建。
TestClass t(__f);
这从 lambda 创建了一个临时 std::function
,将其传递给 TestClass
构造函数,然后销毁临时。
在这一行之后,对 .F()
的调用在悬空引用之后结束,并导致未定义的行为。
您的核心问题可能是您认为 lambda 是 std::function
。它不是。一个 std::function
可以存储一个 lambda。
你的第二个问题是将某些东西定义为 const&
,这几乎总是一个非常愚蠢的想法。引用的行为在根本上与值不同。
你的第三个问题是变量名中的双下层存储。 (或者以 _
后跟大写字母开头的标识符)。
如果您想知道 std::function
是如何工作的以及它是什么,有很多关于该主题的优秀 SO 帖子,其中包含各种级别的技术细节。
注意(1)fn
定义为reference(to const); (2) lambda 和 std::function
不是同一类型; (3) 不能直接绑定不同类型对象的引用。
对于第一种情况,
TestClass t([](std::string str) {std::cout << str << std::endl; });
t.F();
创建了一个临时的 lambda,然后将其转换为 std::function
,这也是一个临时的。临时std::function
绑定到构造函数的参数_f
,绑定到成员f
。这条语句后临时对象将被销毁,然后 f
变为悬挂,当 t.F();
时失败。
对于第二种情况,
fn __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();
创建了一个临时 lambda,然后将其绑定到引用(到 const)。然后把它的生命周期延长到引用的生命周期__f
,这样代码就可以了
对于第3种情况,
auto __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();
lambda 被创建然后转换为 std::function
这是一个临时的。临时std::function
绑定到构造函数的参数_f
,绑定到成员f
。这条语句后临时对象将被销毁,然后 f
变为悬挂,当 t.F();
时失败。
(1) 您可以像 typedef std::function<void(std::string)> fn;
一样将 fn
声明为非引用,然后 std::function
将被复制并且每个案例都会正常工作。
(2) 不要使用双下划线开头的名称,它们在 C++ 中是保留的。
我有一个 class,它将 std::function
作为参数,我分配了一个 lambda 函数。它在构造函数中工作,但之后停止工作。调试器在第一行 运行 之后说 f
是 "empty"。为什么?
#include <iostream>
#include <string>
#include <functional>
typedef std::function<void(std::string)> const& fn;
class TestClass
{
public:
TestClass(fn _f) : f(_f) { F(); }
void F() { f("hello"); };
private:
fn f;
};
int main()
{
TestClass t([](std::string str) {std::cout << str << std::endl; });
t.F();
return 0;
}
调用t.F()
导致错误。为什么?
我改成下面这样就可以解决:
int main()
{
fn __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();
return 0;
}
但同样,当我将 fn
更改为 auto
时,这不起作用!
int main()
{
auto __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();
return 0;
}
发生这种情况的原因是什么?
typedef std::function<void(std::string)> const& fn;
这不是 std::function
,它是对 std::function
的引用。
TestClass(fn _f) : f(_f) { F(); }
fn f;
在这里你把一个 const&
绑定到一个 std::function
并将它绑定到另一个 const&
到一个 std::function
。构造函数主体中的 F()
起作用,因为至少只要构造函数有效,引用就有效。
TestClass t([](std::string str) {std::cout << str << std::endl; });
这将创建一个从 lambda 创建的 std::function
临时文件。这个临时的持续时间与当前行一样长(直到 ;
)。
然后临时std::function
被丢弃
由于 TestClass
比 const&
多了 std::function
,它不会延长临时对象的生命周期。
所以在该行之后,对 std::function const&
的任何调用都是未定义的行为,您会在稍后对 .F()
的调用中看到这一点。
fn __f = [](std::string str) {std::cout << str << std::endl; };
这确实涉及延长寿命。从 lambda 创建的临时 std::function
的生命周期延长到 __f
变量的生命周期。
顺便说一句,此行还使您的程序格式错误,不需要诊断,因为它有一个包含双下划线的变量。这些标识符是为编译器的实现保留的,您不能创建它们。
TestClass t(__f);
然后我们传递这个引用(指的是延长生命周期的临时),一切正常。
auto __f = [](std::string str) {std::cout << str << std::endl; };
这将创建一个变量 __f
(见上文,错误名称),它是一个 lambda。
lambda 不是 std::function
。 std::function
可以从 lambda 隐式创建。
TestClass t(__f);
这从 lambda 创建了一个临时 std::function
,将其传递给 TestClass
构造函数,然后销毁临时。
在这一行之后,对 .F()
的调用在悬空引用之后结束,并导致未定义的行为。
您的核心问题可能是您认为 lambda 是 std::function
。它不是。一个 std::function
可以存储一个 lambda。
你的第二个问题是将某些东西定义为 const&
,这几乎总是一个非常愚蠢的想法。引用的行为在根本上与值不同。
你的第三个问题是变量名中的双下层存储。 (或者以 _
后跟大写字母开头的标识符)。
如果您想知道 std::function
是如何工作的以及它是什么,有很多关于该主题的优秀 SO 帖子,其中包含各种级别的技术细节。
注意(1)fn
定义为reference(to const); (2) lambda 和 std::function
不是同一类型; (3) 不能直接绑定不同类型对象的引用。
对于第一种情况,
TestClass t([](std::string str) {std::cout << str << std::endl; });
t.F();
创建了一个临时的 lambda,然后将其转换为 std::function
,这也是一个临时的。临时std::function
绑定到构造函数的参数_f
,绑定到成员f
。这条语句后临时对象将被销毁,然后 f
变为悬挂,当 t.F();
时失败。
对于第二种情况,
fn __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();
创建了一个临时 lambda,然后将其绑定到引用(到 const)。然后把它的生命周期延长到引用的生命周期__f
,这样代码就可以了
对于第3种情况,
auto __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();
lambda 被创建然后转换为 std::function
这是一个临时的。临时std::function
绑定到构造函数的参数_f
,绑定到成员f
。这条语句后临时对象将被销毁,然后 f
变为悬挂,当 t.F();
时失败。
(1) 您可以像 typedef std::function<void(std::string)> fn;
一样将 fn
声明为非引用,然后 std::function
将被复制并且每个案例都会正常工作。
(2) 不要使用双下划线开头的名称,它们在 C++ 中是保留的。