You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

std::optional的emplace操作机制及三种调用方式差异探究

std::optional::emplace的析构调用与操作类型解析

问题背景

在使用std::optional的emplace函数原地创建对象时,发现类的析构函数被调用,想明确该函数对类对象执行的是拷贝还是移动操作;同时三种不同的emplace调用方式中,两种会触发析构,希望了解三者的差异。

基础类代码

class myClass{
private:
   int m_num;
public:
   myClass(int num) : m_num(num){}
   ~myClass(){
      std::cout << "myClass destructed\n";
   }
};

主函数代码

int main(){
   std::optional<myClass> a;
   std::optional<myClass> b;
   std::optional<myClass> c;

   std::cout << "emplacing a: \n";
   a.emplace(myClass{0});
   std::cout << "emplacing b: \n";
   b.emplace<myClass>(0);
   std::cout << "emplacing c: \n";
   c.emplace(0);
   std::cout << "done\n";
   
   while (true) {};    
}

程序输出

emplacing a: 
myClass destructed
emplacing b:
myClass destructed
emplacing c:
done

解答

1. emplace的操作类型:拷贝还是移动?

std::optional::emplace的核心是原地构造对象,具体行为由传入的参数决定:

  • 若传入临时对象(如myClass{0}),emplace会优先调用移动构造函数,在std::optional的内部存储空间中构造对象;若类未定义移动构造函数,则会调用默认生成的拷贝构造函数。临时对象完成构造任务后生命周期结束,触发析构。
  • 若直接传入类构造所需的原始参数(如0),emplace会直接用这些参数在std::optional内部原地构造对象,全程无额外的拷贝/移动操作,自然不会触发额外的析构。

2. 三种调用方式的差异

方式一:a.emplace(myClass{0});

先显式创建一个临时myClass对象myClass{0},再将该临时对象作为参数传给emplace。emplace借助这个临时对象,通过移动/拷贝构造完成std::optional内部对象的创建。临时对象随后销毁,因此打印析构日志。

方式二:b.emplace<myClass>(0);

显式指定了emplace的模板参数myClass,此时函数会先以0为参数隐式构造一个临时myClass对象,再通过移动/拷贝构造完成std::optional内部对象的创建。临时对象销毁后触发析构。这种写法属于冗余操作——std::optional已经明确存储类型为myClass,显式指定模板参数反而会生成额外的临时对象。

方式三:c.emplace(0);

这是emplace的标准用法:直接传入myClass构造所需的参数0,emplace直接在std::optional的内部存储空间调用myClass(0)构造对象,全程无额外临时对象生成,因此不会触发额外的析构。


内容的提问来源于stack exchange,提问作者green blanket

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 08:57:17