通过lambda调用成员函数时意外未初始化的数据

时间:2017-04-08 10:31:57

标签: c++ lambda this

考虑以下简短程序。我使用std::function和lambda将成员函数对象(initialise)设置为同一个类(initialiser)内的成员函数(A)。然后在4个地方调用initialise():1。在A的{​​{1}}的构造函数中,2。直接来自main,3。{{1}的构造函数来自main的构造函数,以及4.直接来自A的构造函数。

B

编译并运行此代码会产生以下意外情况(至少对我而言)。

B

有人可以澄清为什么最后一次拨打 #include <iostream> #include <functional> #include <vector> class A { public: A(std::vector<int> const& sizes) : m_sizes(sizes) { set_initialiser(); initialise(); } A() = default; std::function<void()> initialise; void set_initialiser() { initialise = [this]() {return initialiser(); }; }; // void set_initialiser() { initialise = std::bind(&A::initialiser, this); }; // the same occurs if I use bind instead of a lambda private: std::vector<int> m_sizes; void initialiser() { std::cout << "m_sizes size = " << m_sizes.size() << ", with contents:"; for (auto & s : m_sizes) std::cout << " " << s; std::cout << std::endl; }; }; class B { public: B(std::vector<int> const& v) { a = A(v); a.initialise(); // here a.m_sizes and a.initialise.functor.this.m_sizes differ }; private: A a; }; int main(int argc, char* argv[]) { auto a = A({ 4,3,2,1 }); a.initialise(); auto b = B({ 4,3,2,1 }); return 0; } 包含未初始化的m_sizes size = 4, with contents: 4 3 2 1 m_sizes size = 4, with contents: 4 3 2 1 m_sizes size = 4, with contents: 4 3 2 1 m_sizes size = 0, with contents: ?我怀疑它必须与lambda中使用的initialise()的不同实例有关,但我不明白为什么从m_sizes调用它并从另一个类调用它应该有什么不同

1 个答案:

答案 0 :(得分:1)

a = A(v);

此行创建一个临时对象A(v)(我称之为t),并将其数据复制到at.initialise包含一个lambda,它按值捕获this(即&t)。然后将此lambda复制到a.initialise;但请注意,它仍然指的是&t

然后,完整的表达式结束,t被销毁(因为它是一个临时的),并且a.initialise内的捕获指针现在正在悬空。 a.initialise()的下一次调用因此取消引用悬空指针,为您提供未定义的行为。

请注意,main中也会发生完全相同的问题(相同的未定义行为),但效果却不同。以下是对原因的推测,但请记住未定义的行为未定义,任何事情都可能发生。

我认为在main内,发生了复制省略,并且A({ 4,3,2,1 })直接在a的空格中构建,使其this与{a的{​​{1}}相同1}},这使得对initialise的调用仍然有效。

B的构造函数中,复制省略是不可能的(因为你正在进行赋值而不是初始化),所以临时确实被破坏了,留下了一个正确破坏的向量,它恰好仍然是相同的在你的情况下一个空的向量(可能是因为它从临时移动到a)。

再一次:这只是推测,代码完全是错误的。