C++闭包底层行为测试中两次移动构造调用的原因问询
C++闭包移动构造触发逻辑说明
测试代码
#include <iostream> #include <iomanip> #include <functional> #include <list> using namespace std; class A { list<int> ints = { 1, 2 }; void display(string msg) const { cout << setw(8) << msg << ": this = 0x" << this << ", ints.front() = " << ints.front() << endl; } public: A() { ints.front() += 1; display("A()"); } A(const A& other) : ints(other.ints) { ints.front() += 1; display("A(A&)"); } A(const A&& other) noexcept : ints(other.ints) { ints.front() += 1; display("A(A&&)"); } ~A() { display("~A()"); } void doSomething() const { display("A doing something"); } }; function<void (void)> fn() { A a; auto rfn = [=]() { cout << "In Lambda: &a = 0x" << &a << endl; a.doSomething(); }; return rfn; } int main() { { auto fn2 = fn(); fn2(); } return 0; }
运行输出(VS2019无优化环境)
A(): this = 0x010FF948, ints.front() = 2 A(A&): this = 0x010FF934, ints.front() = 3 A(A&&): this = 0x010FF838, ints.front() = 4 A(A&&): this = 0x010FFA4C, ints.front() = 5 ~A(): this = 0x010FF838, ints.front() = 4 ~A(): this = 0x010FF934, ints.front() = 3 ~A(): this = 0x010FF948, ints.front() = 2 In Lambda: &a = 0x010FFA4C A doing something: this = 0x010FFA4C, ints.front() = 5 ~A(): this = 0x010FFA4C, ints.front() = 5
两次移动构造的触发原因
以下分析基于*Visual Studio 2019默认无优化(/Od)*的std::function实现,开启O2优化后编译器会触发复制消除省略这两次移动操作:
- 第一次移动构造:fn()的返回值类型为
std::function<void(void)>,而代码中定义的rfn是匿名lambda类型,需要先做类型转换:局部lambda对象是return语句中的右值,移动构造为临时std::function对象时,lambda内部存储的A成员会被移动到std::function的内部存储中,对应地址0x010FF838的A(A&&)输出。 - 第二次移动构造:fn()返回的临时
std::function对象属于右值,在main函数中构造fn2变量时,会调用std::function的移动构造函数,将其内部存储的A成员移动到fn2的内部存储中,对应地址0x010FFA4C的A(A&&)输出。
两次移动完成后,中间临时存储的A对象(lambda内的A、第一次构造的std::function内的A)会依次析构,最终fn2中存储的A对象会在作用域结束后析构。
内容的提问来源于stack exchange,提问作者melston
相关产品推荐
相关产品推荐

