gpt4 book ai didi

c++ - 使用 std::function 作为成员函数,捕获 `this` ,并在析构函数之后从复制的 lambda 访问它

转载 作者:太空狗 更新时间:2023-10-29 21:11:23 26 4
gpt4 key购买 nike

Flex Ferrum发布代码示例 here (我觉得Minimal, Complete, and Verifiable就够了):

#include <iostream>
#include <functional>
using namespace std;

class Bang
{
public:
Bang(int i = 0) : m_val(i)
{
m_foo = [this] {std::cout << m_val << std::endl;};
}

~Bang()
{
m_val = -1;
}

void Foo()
{
m_foo();
}
private:
int m_val;
std::function<void ()> m_foo;
};

Bang GetBang()
{
return Bang(100500);
}

int main() {
Bang b(100500);
b.Foo();
b = GetBang();
b.Foo();
return 0;
}

我们漂亮的 Flex 还提供 live demo


粗略看了一下,我以为会输出100500,但真正的输出是:

-1
  • 为什么?它的背后是什么?
  • 如何解决?(输出100500,而不是-1)

我在提问框中写了一些我自己的推理,但发现作为答案张贴更合适(会使问题太长)。如果我的回答有误,欢迎指正,欢迎补充

最佳答案

啊,应该归咎于临时的析构函数- Bang(100500),返回形式GetBang,是prvalue , 并且有 temporary object lifetime .

  1. [this] will be stored as reference of *this ,像这样:

    class Lambda
{
public:
void operator()() const
{
//output
}

private:
Bang& bang;

public:
Lambda(Bang& bang) : bang{bang}
{
}

} lambda{*this};
...
m_foo = lambda;

  1. 因为这里没有RVO,所以,临时的Bang(100500)会先赋值给b ,然后被销毁。

  2. 自定义 operator()constructordestructor 输出一些信息:


#include <iostream>
#include <functional>

using namespace std;

class Bang
{
public:
Bang(int i = 0) : m_val(i)
{

std::cout << "Bang(int i = 0) m_val address is " << &m_val << '\n';
class Lambda
{
public:
void operator()() const
{

std::cout << "operator() m_val address is " << &bang.m_val << '\n';
std::cout << bang.m_val << std::endl;
}

private:
Bang &bang;

public:
Lambda(Bang &bang) : bang{bang}
{
}

} lambda{*this};
m_foo = lambda;

}

~Bang()
{
std::cout << "~Bang()\n";
m_val = -1;
}

void Foo()
{
m_foo();
}

private:
int m_val;
std::function<void()> m_foo;
};

Bang GetBang()
{
return Bang(100500);
}

int main()
{
Bang b;
b = GetBang();
b.Foo();
return 0;
}

live demo

输出:

Bang(int i = 0) m_val address is 0x7ffd202c48b0
Bang(int i = 0) m_val address is 0x7ffd202c48e0
~Bang()
operator() m_val address is 0x7ffd202c48e0
-1
~Bang()

显示:

  • dtor 会在输出之前被调用,这意味着临时对象已经被销毁。
  • m_value 的地址没有改变。

这两个保证我们仍然可以从 bm_foo() 访问临时的 m_value

访问已销毁的对象应该是未定义行为,但不需要警告和错误。

更新

要解决这个问题,有两种方案:

  1. 喜欢@Killzone Kid指出,使用初始化程序捕获:[bang = *this]。这需要 c++14。
  2. 通过复制捕获当前对象的更简单方法:[*this]。这需要 c++17。 live demo

关于c++ - 使用 std::function 作为成员函数,捕获 `this` ,并在析构函数之后从复制的 lambda 访问它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50773023/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com