news 2026/4/3 4:59:11

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/3/29 3:40:27

SpringBoot+Vue 大学生就业需求分析系统平台完整项目源码+SQL脚本+接口文档【Java Web毕设】

摘要 随着高等教育普及率的提升&#xff0c;大学生就业问题日益成为社会关注的焦点。就业市场供需不平衡、信息不对称等问题导致大学生就业压力增大&#xff0c;亟需一种高效、智能的分析工具来辅助就业决策。传统就业信息平台功能单一&#xff0c;缺乏数据驱动的需求分析能力…

作者头像 李华
网站建设 2026/3/31 6:49:01

Java SpringBoot+Vue3+MyBatis Spring Boot企业员工薪酬关系系统系统源码|前后端分离+MySQL数据库

摘要 随着信息技术的快速发展&#xff0c;企业管理的数字化转型已成为提升运营效率的关键。薪酬管理作为企业人力资源管理的核心模块&#xff0c;传统的手工操作或单机版管理系统已无法满足现代企业对数据实时性、安全性和协同性的需求。尤其是在员工规模较大的企业中&#xf…

作者头像 李华
网站建设 2026/3/28 16:43:12

腾讯超算中心AI资源调度:架构师如何支持游戏AI应用?

腾讯超算中心AI资源调度揭秘:架构师如何为游戏AI保驾护航? 引言:游戏AI的“算力痛点”,你遇到过吗? 作为游戏开发架构师,你是否曾面临这样的困境: 想给游戏加个智能NPC(比如《王者荣耀》的AI队友),但训练10亿条对战数据需要占用100台GPU服务器,成本高得吓人; 上线…

作者头像 李华