通过 lambda 调用成员函数时意外未初始化的数据
Unexpectedly uninitialised data in call to member function through lambda
考虑下面的简短程序。我正在使用std::function
和lambda将成员函数对象(initialise
)设置为同一类(A
)中的成员函数(initialiser
)。 然后initialise()
在 4 个地方被调用:1. 在来自main
的A
的构造函数中,2. 直接从main
调用,3. 在A
的构造函数中来自B
的构造函数中,以及 4. 直接从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:
谁能阐明为什么最后一次调用initialise()
包含未初始化的m_sizes
?我怀疑它一定与 lambda 中使用的不同this
实例有关,但我不明白为什么从main
调用它和从另一个类调用它应该有任何区别。
a = A(v);
此行创建一个临时对象A(v)
(我称之为t
),并将其数据复制到a
中。t.initialise
包含一个按值捕获this
(即&t
)的 lambda。然后,将此 lambda 复制到a.initialise
中;但是请注意,它仍然指的是&t
.
然后,完整的表达式结束,t
被销毁(因为它是临时的),并且捕获的指针现在悬空a.initialise
。因此,下一次调用a.initialise()
会取消引用一个悬空的指针,从而为您提供未定义的行为。
请注意,完全相同的问题(相同的未定义行为)也发生在您的main
中,但其效果不同。以下是对原因的猜测,但请记住,未定义的行为是未定义的,任何事情都可能发生。
我假设在main
内部,复制省略发生,临时A({ 4,3,2,1 })
直接在a
空间中构造,使其this
与a
相同,这使得对initialise
的调用仍然有效。
在B
的构造函数中,复制 elision 是不可能的(因为你是在做赋值而不是初始化),所以临时确实被销毁了,留下了一个正确销毁的向量,它恰好仍然与你情况下的空向量相同(可能是因为它从临时向量移动到a
)。
再说一遍:这只是猜测,代码是完全错误的。
- 如何使用数据对象上的常量指针初始化类
- 如何在 malloc 内存中初始化非 POD 数据
- 内联静态数据的初始化
- 是否可以使用右值初始化数据成员?
- 构造函数正在初始化数据成员
- 初始化数据成员取决于构造函数中的条件
- openGL如何处理发送给它的空/未初始化数据?
- 如何在C 11中正确初始化数据成员
- 在具有初始化的声明中,是否可以使用对未初始化数据的引用
- 使用值初始化的数组对零初始化数据
- 在C++中,如果我们不在构造函数中初始化数据成员,那么类中数据成员的值会是多少
- 为什么我们需要构造函数 C++,我们可以通过函数(初始化数据或访问私有数据成员)来做同样的事情
- 正在初始化数据文件中的静态常量成员
- 用成员函数初始化数据成员
- 在不初始化数据的情况下调整 std::vector C++的大小<char>
- 尝试在构造函数中初始化数据成员,但失败.为什么
- 在c++中使用初始化列表初始化数据成员引用
- 如何创建一个可以初始化c++数据类型的类
- 当初始化数据类型char*和非char*时
- 代码是否被视为可移植可执行文件格式中的初始化数据,初始化数据和单元化数据之间的确切区别是什么