考虑以下简短程序。我使用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
调用它并从另一个类调用它应该有什么不同
答案 0 :(得分:1)
a = A(v);
此行创建一个临时对象A(v)
(我称之为t
),并将其数据复制到a
。 t.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
)。
再一次:这只是推测,代码完全是错误的。