考虑下面的简短程序。我使用std::function
和lambda将成员函数对象(initialise
)设置为同一类(initialiser
)中的成员函数(A
)。然后在4个地方调用initialise()
:1.在A
中的main
构造函数中; 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
包含一个lambda,该lambda已按值捕获了this
(即&t
)。然后,您将此lambda复制到a.initialise
中;但是请注意,它仍然引用&t
。然后,完整表达式结束,
t
被销毁(因为它是临时的),并且a.initialise
中捕获的指针现在悬空了。因此,对a.initialise()
的下一次调用取消了悬空指针的引用,从而为您提供了不确定的行为。请注意,您的
main
也发生了完全相同的问题(相同的未定义行为),但是其影响有所不同。接下来是对原因的推测,但请记住,未定义的行为是未定义的,任何事情都可能发生。我想在
main
内部会发生复制省略,并且直接在A({ 4,3,2,1 })
的空间中构造临时a
,使其this
与a
相同,这使得对initialise
的调用仍然有效。在
B
的构造函数中,不可能进行复制省略(因为您正在执行赋值操作而不是初始化),因此临时确实被销毁了,留下了正确销毁的 vector ,在您的情况下,该 vector 仍然与空 vector 相同(可能是因为它已从临时目录移到a
)。再说一遍:这仅仅是猜测,而代码根本就是错误的。