news 2026/5/6 2:20:02

C++ 中emplace系列函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ 中emplace系列函数
  • emplace的原地构造核心是定位 new(placement new):在容器已分配的内存地址上,直接调用元素的构造函数创建对象;
  • 借助完美转发传递构造参数,自动匹配元素的对应构造函数,无需提前创建临时对象;
  • 相比push系列函数,emplace避免了临时对象的创建、拷贝 / 移动和销毁,效率更高(尤其适合不可拷贝对象如std::thread、大对象如std::function)。

一、核心结论先明确

emplace系列函数(emplace/emplace_back/emplace_front等)确实是直接调用元素的构造函数,并且是在容器为元素分配好的内存空间里 “就地” 构造,全程不会创建临时对象,这也是它比push/push_back更高效的核心原因。

二、emplace 的原地构造实现原理

要理解原地构造,我们先对比push_backemplace_back的区别,再拆解底层实现:

1. 先看 “非原地构造” 的例子(push_back)

比如给vector<std::thread>添加元素时用push_back

// 错误示例:std::thread不可拷贝,push_back会先创建临时thread,再尝试移动/拷贝 // 即使能移动,也会多一次临时对象的创建+销毁 std::vector<std::thread> vec; vec.push_back(std::thread([]() { /* 线程逻辑 */ }));

push_back的执行流程:

  1. 先在当前代码行创建一个临时的std::thread对象(调用std::thread的构造函数);
  2. 容器(vector)为新元素分配内存;
  3. 将临时对象移动 / 拷贝到容器分配的内存中;
  4. 销毁临时对象(调用std::thread的析构函数)。

简单说:push_back是 “先造好对象→再搬到容器里”,中间多了临时对象的开销。

2. 再看 “原地构造” 的例子(emplace_back)

还是上面的场景,用emplace_back

std::vector<std::thread> vec; // emplace_back直接在vector的内存里构造thread对象 vec.emplace_back([]() { /* 线程逻辑 */ });

emplace_back的执行流程:

  1. 容器(vector)先为新元素分配好内存空间(确定内存地址);
  2. emplace_back接收构造std::thread所需的参数(这里是 lambda),通过完美转发std::forward)把参数传递到第一步分配的内存地址上;
  3. 定位 new(placement new)在该内存地址上直接调用std::thread的构造函数,创建对象;
  4. 全程无临时对象,无拷贝 / 移动。
3. 底层关键:定位 new(placement new)

emplace的原地构造核心依赖 C++ 的定位 new语法,它的作用是 “在指定的内存地址上创建对象”,语法形式很简单:

// 普通new:分配内存 + 构造对象 T* ptr = new T(参数); // 定位new:仅在已分配的内存上构造对象(不分配新内存) void* mem = 已分配的内存地址; T* obj = new (mem) T(参数); // 直接在mem指向的地址调用T的构造函数

容器的emplace函数内部,就是用这种方式:先通过容器的内存管理逻辑(比如 vector 的扩容、queue 的节点分配)拿到一块空内存,再用定位 new 调用元素的构造函数,把对象 “造” 在这块内存上。

4. 结合线程池代码的例子

这两行emplace,都是典型的原地构造:

// 1. workers.emplace_back([this]() { ... }) workers.emplace_back([this]() { /* 线程逻辑 */ }); // 原理:vector先为新的thread分配内存,然后直接在该内存上调用std::thread的构造函数(参数是lambda),无临时thread对象 // 2. tasks.emplace(std::move(task)) tasks.emplace(std::move(task)); // 原理:queue先为新的std::function<void()>分配节点内存,然后直接在该内存上调用std::function的移动构造函数,无临时function对象

三、emplace 为什么能匹配任意构造函数?

你可能会问:emplace怎么知道要调用元素的哪个构造函数?答案是完美转发(std::forward)emplace系列函数都是模板函数,会接收任意数量、任意类型的参数,然后通过std::forward把参数 “原样” 传递给元素的构造函数,自动匹配对应的构造版本。

比如:

  • 如果你传[](){}emplace_back,就匹配std::thread的 “接收可调用对象” 的构造函数;
  • 如果你传std::move(task)emplace,就匹配std::function的移动构造函数;
  • 如果你传10, "hello"emplace,就匹配元素的 “接收 int+const char*” 的构造函数(如果有的话)。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/3 14:02:24

C语言 结构体

本文介绍了C语言中结构体的基本概念和使用方法。主要内容包括&#xff1a;1.结构体声明语法和成员访问方式&#xff1b;2.结构体内存对齐规则及其对空间利用的影响&#xff1b;3.通过示例展示了不同成员排列顺序对结构体大小的影响&#xff1b;4.结构体位段的使用方法及其与普通…

作者头像 李华
网站建设 2026/5/3 19:07:30

Linux 系统下 Oracle AI Database 26ai 环境部署全解析

Oracle AI Database 26ai 作为融合 AI 能力的数据平台&#xff0c;正受到数据库管理员和 AI 开发人员的广泛关注。在开发测试场景中&#xff0c;无需构建复杂的高可用架构&#xff0c;通过精简部署流程&#xff0c;单机环境即可快速体验其核心 AI 特性。本文将系统讲解在 Linux…

作者头像 李华
网站建设 2026/4/27 17:34:40

RMBG-2.0轻量模型原理简析:如何在小参数量下实现发丝级分割

RMBG-2.0轻量模型原理简析&#xff1a;如何在小参数量下实现发丝级分割 1. 为什么你需要一个“能看清头发”的抠图工具 你有没有试过用传统抠图工具处理一张带飘逸发丝的证件照&#xff1f;边缘毛躁、半透明区域糊成一片、发丝和背景粘连——最后不得不花半小时手动擦除&…

作者头像 李华
网站建设 2026/5/2 22:48:29

小白友好!Nano-Banana极简纯白风格入门指南,3步出效果

小白友好&#xff01;Nano-Banana极简纯白风格入门指南&#xff0c;3步出效果 你是不是也遇到过这些情况&#xff1f; 想给新设计的服装做一张专业级展示图&#xff0c;但不会用PS&#xff0c;更搞不定3D建模软件&#xff1b;看到别人生成的爆炸图、技术蓝图惊艳不已&#xf…

作者头像 李华
网站建设 2026/5/1 8:09:55

Swin2SR交互指南:左侧面板上传与右侧结果查看

Swin2SR交互指南&#xff1a;左侧面板上传与右侧结果查看 1. 这不是普通放大&#xff0c;是AI显微镜在工作 你有没有试过把一张模糊的截图、马赛克严重的表情包&#xff0c;或者AI生成后只有512像素的小图&#xff0c;直接拉大到打印尺寸&#xff1f;结果往往是——满屏锯齿、…

作者头像 李华