【发布时间】:2020-10-27 01:48:16
【问题描述】:
std::function 构造函数在存储在容器中时究竟做了什么?
在此测试代码中:
struct A {
int sn;
A() = delete;
A(int v) : sn(v) { cout << "A::init(" << sn << ')' << endl; }
A(const A& a) : sn(a.sn+1) { cout << "A::copy(" << sn << ')' << endl; }
A(A&& a) : sn(a.sn+1) { cout << "A::move(" << sn << ')' << endl; }
~A() { cout << "A::delete(" << sn << ')' << endl; }
};
void func(int a, A &b) {
cout << "func2:" << a << ',' << b.sn << endl;
}
int main(int argc, char *argv[]) {
std::vector<std::function<void(void)>> fv;
A a(1);
cout << "call bind()" << endl;
fv.emplace_back(std::bind(func,1,a));
fv.front()();
cout << "end of local scope" << endl;
}
我声明class A 的移动和复制构造函数来累积一个序列号,这样我就可以将它作为第N 个创建的实例来跟踪。结果是:
A::init(1)
call bind()
A::copy(2)
A::move(3)
A::move(4)
A::delete(3)
A::delete(2)
func2:1,4
end of local scope
A::delete(1)
A::delete(4)
首先在main() 本地范围内创建实例a(1),然后在调用std::bind() 时将其复制为新实例a(2),然后再次移动到std::function 动态分配的内存(或其内部内存片段)中?) 保存std::bind() 实例的副本,所以我们有A 的第三个实例由std::function 实例保存。
目前为止都可以理解,但是为什么还有另一个移动构造呢?以及为什么第三个实例在第二个实例之前被销毁,应该在std::bind()返回时清除?
如果我将main() 函数重写为:
int main(int argc, char *argv[]) {
A a(1);
cout << "call bind()" << endl;
std::function<void(void)> f(std::bind(func,1,a));
f();
cout << "end of local scope" << endl;
}
那么结果将是:
A::init(1)
call bind()
A::copy(2)
A::move(3)
A::delete(2)
func2:1,3
end of local scope
A::delete(3)
A::delete(1)
没有第二步构造,一切看起来都很合理。
在这种情况下调用emplace_back() 创建std::function 实例时究竟发生了什么?
【问题讨论】:
-
不是向量,也不是
std::function。这是std::bind。它的语义主要是实现定义的,并且它的参数可能会在他们安顿下来之前被玩弄一下...... ,在您的调试器中,通过这里发生的所有事情,并确定每次移动或复制的原因和原因。 -
如果您将
emplace_back更改为push_back,您可能会觉得更奇怪。在我的快速测试中,第二步被省略了,在这种情况下,push_back看起来比emplace_back更有效率。 -
@SamVarshavchik 不,是
std::function/emplace_back。更具体地说,它是完美的转发,表明它实际上并不完美。
标签: c++ std-function stdbind