使用std::variant与std::monostate时结构体为何被析构两次?
std::variant中A类构造1次、析构2次的原因解析
先看你的代码:
#include <iostream> #include <variant> struct A { A() { std::cout << "Constructing A\n"; } ~A() { std::cout << "Destructing A\n"; } }; struct B { B() { std::cout << "Constructing B\n"; } ~B() { std::cout << "Destructing B\n"; } }; int main() { std::variant<std::monostate, A, B> ab; ab = A(); }
运行输出:
Constructing A Destructing A Destructing A
原因拆解
两次析构对应两个不同的A对象,只是第二个A对象的构造日志被隐藏了——它是通过编译器默认生成的移动/拷贝构造函数创建的,而你没给A定义带输出的移动/拷贝构造,所以看不到对应的构造信息。具体步骤:
- variant初始化阶段:
std::variant<std::monostate, A, B> ab;默认激活std::monostate,不会触发A或B的构造。 - 赋值语句执行阶段:
- 首先创建临时A对象:
A()触发构造函数,输出Constructing A。 std::variant的赋值操作会把这个临时A对象移动(或拷贝)到自身内部,替换原来的monostate。因为编译器生成的默认移动/拷贝构造函数没有输出语句,所以这一步看不到构造日志,但确实创建了第二个A对象。- 临时A对象在赋值语句执行完成后立即被销毁,触发第一次析构,输出
Destructing A。
- 首先创建临时A对象:
- 程序结束阶段:main函数执行完毕,variant对象
ab被销毁,其内部存储的A对象也随之析构,触发第二次析构,输出Destructing A。
验证方法
给A添加移动构造函数并加上输出:
A(A&&) { std::cout << "Moving A\n"; }
重新运行代码,输出会变成:
Constructing A Moving A Destructing A Destructing A
这样就能清晰看到第二个A对象是通过移动构造创建的,也就解释了两次析构的来源。
内容的提问来源于stack exchange,提问作者Chiel
相关产品推荐
相关产品推荐

