emplace与insert的区别(C++11)
转自时习之
C++11中大部分的容器对于添加元素除了传统的 insert 或者 pusb_back/push_front 之外都提供一个新的函数叫做 emplace。 比如如果你想要向 std::vector 的末尾添加一个数据,你可以:
std::vector<int> nums;
nums.push_back(1);
你也可以使用:
std::vector<int> nums;
nums.empace_back(1);
避免不必要的临时对象的产生
emplace 最大的作用是避免产生不必要的临时变量,因为它可以完成 in place 的构造,举个例子:
struct Foo {
Foo(int n, double x);
};
std::vector<Foo> v;
v.emplace(someIterator, 42, 3.1416); // 没有临时变量产生
v.insert(someIterator, Foo(42, 3.1416)); // 需要产生一个临时变量
v.insert(someIterator, {42, 3.1416}); // 需要产生一个临时变量
这是 \(emplace\) 和 \(insert\) 最大的区别点。\(emplace\) 的语法看起来不可思议,在上 面的例子中后面两个参数自动用来构造 vector 内部的 Foo 对象。做到这一点主要 使用了 C++11 的两个新特性 \(变参模板\) 和 \(完美转发\)。”变参模板”使得 emplace 可以接受任意参数,这样就可以适用于任意对象的构建。
”完美转发”使得接收下来的参数 能够原样的传递给对象的构造函数,这带来另一个方便性就是即使是构造函数声明为 \(explicit\) 它还是可以正常工作,因为它不存在临时变量和隐式转换。
struct Bar {
Bar(int a) {}
explicit Bar(int a, double b) {}
};
int main(void)
{
vector<Bar> bv;
bv.push_back(1); // 隐式转换生成临时变量
bv.push_back(Bar(1)); // 显示构造临时变量
bv.emplace_back(1); // 没有临时变量
//bv.push_back({1, 2.0}); // 无法进行隐式转换
bv.push_back(Bar(1, 2.0)); // 显示构造临时变量
bv.emplace_back(1, 2.0); // 没有临时变量
return 0;
}
map 的特殊情况
\(map\) 类型的 \(emplace\) 处理比较特殊,因为和其他的容器不同,map 的 emplace 函数把它接收到的所有的参数都转发给 \(pair\) 的构造函数。对于一个 \(pair\) 来说,它既需要构造它的 \(key\) 又需要构造它的 \(value\)。如果我们按照普通的 的语法使用变参模板,我们无法区分哪些参数用来构造 \(key\), 哪些用来构造 \(value\)。 比如下面的代码:
map<string, complex<double>> scp;
scp.emplace("hello", 1, 2); // 无法区分哪个参数用来构造 key 哪些用来构造 value
// string s("hello", 1), complex<double> cpx(2) ???
// string s("hello"), complex<double> cpx(1, 2) ???
所以我们需要一种方式既可以接受异构变长参数,又可以区分 key 和 value,解决 方式是使用 C++11 中提供的 tuple。
pair<string, complex<double>> scp(make_tuple("hello"), make_tuple(1, 2));
然后这种方式是有问题的,因为这里有歧义,第一个 tuple 会被当成是 key,第二 个tuple会被当成 value。最终的结果是类型不匹配而导致对象创建失败,为了解决 这个问题,C++11 设计了 piecewise_construct_t 这个类型用于解决这种歧义,它 是一个空类,存在的唯一目的就是解决这种歧义,全局变量 std::piecewise_construct 就是该类型的一个变量。所以最终的解决方式如下:
pair<string, complex<double>> scp(piecewise_construct,
make_tuple("hello"),
make_tuple(1, 2));
当然因为 map 的 emplace 把参数原样转发给 pair 的构造,所以你需要使用同样 的语法来完成 emplace 的调用,当然你可以使用 forward_as_tuple 替代 make_tuple,该函数会帮你构造一个 tuple 并转发给 pair 构造。
map<string, complex<double>> scp;
scp.emplace(piecewise_construct,
forward_as_tuple("hello"),
forward_as_tuple(1, 2));
所以对于 map 来说你虽然避免了临时变量的构造,但是你却需要构建两个 tuple 。 这种 traedoff 是否值得需要代码编写者自己考虑,从方便性和代码优雅性上来说:
scp.insert({"world", {1, 2}});
这种写法都要胜过前面这个 emplace 版本。所以个人认为对于临时变量构建代价不是 很大的对象(比如基础类型)推荐使用 insert 而不是 emplace。
emplace与insert的区别(C++11)的更多相关文章
- 关于thymeleaf th:replace th:include th:insert 的区别
关于thymeleaf th:replace th:include th:insert 的区别 th:insert :保留自己的主标签,保留th:fragment的主标签. th:re ...
- vector的 emplace 和 insert 以及使用vector进行iterator遍历 且 erase的时候注意事项
vector<int> first;//Size()==2 first.push_back(); first.push_back(); //first.insert(2); vector& ...
- python中append、extend、和insert的区别
a_list = [x for x in range(1, 11)] print(a_list) a_list.append('sdadfewf') # 将整个字符串放到列表的最后 print(a_l ...
- SELECT INTO 和 INSERT INTO区别
(1).SELECT * INTO 新表名 FROM 旧表名 (2).INSERT INTO 新表名(列名1,列名2) SELECT * FROM 旧表名 第一句新表名不存在会自动创建, 第二句需创建 ...
- append、extend与insert的区别
最近在自学Python语言,看到向列表增加更多数据时被append(),extend(),insert()方法绕晕了. 作为编程0基础的小白,觉得有必要自己再梳理一遍: 1.append()方法是指在 ...
- python中List append()、extend()和insert()的区别
Python中向列表增加更多数据时,有append().extend()和insert()等方法 其中最常用的是list.append(obj) 向列表的尾部添加一个新的元素. 需要一次性添加多个元素 ...
- mongodb - save()和insert()的区别
遇到_id相同的情况下:insert操作会报错:save完成保存操作 > db.person.find() > db.person.insert({"_id":1,ag ...
- Hive之insert into与insert overwrite区别
一.实践先行,直接上手 1. hive 表及数据准备 建表,并插入初始数据.向表中插入 hive> use test; hive> create table kwang_test (id ...
- 【C++】朝花夕拾——STL vector
STL之vector篇 N久之前是拿C的数组实现过vector中的一些简单功能,什么深拷贝.增删查找之类的,以为vector的实现也就是这样了,现在想想真是...too young too naive ...
随机推荐
- kafka-connect-hdfs重启,进去RECOVERY状态,从hadoop hdfs拿租约,很正常,但是也太久了吧
虽说这个算是正常现象,等的时间也太久了吧.分钟级了.这个RECOVERY里面的WAL有点多余.有这么久的时间,早从新读取kafka写入hdfs了.纯属个人见解. @SuppressWarnings(& ...
- vue中父组件调用子组件函数
用法: 子组件上定义ref="refName", 父组件的方法中用 this.$refs.refName.method 去调用子组件方法 详解: 父组件里面调用子组件的函数,父组 ...
- Python之Requests库的7个主要方法
方法 说明 requests.request() 构造一个请求,支撑一下各方法的基础方法 requests.get() 获取HTML网页的主要方法 requests.head() 获取网页头信息的方法 ...
- WiFi-ESP8266入门http(1)-建立服务器,直接发送网
#include <ESP8266WiFi.h> /*** 该工程可以在2.4.0版本esp8266库中运行,没在更高版本库中进行测试 ***/ const char *ssid = &q ...
- ssm框架整合+Ajax异步验证
SSM框架是目前企业比较常用的框架之一,它的灵活性.安全性相对于SSH有一定的优势.说到这,谈谈SSM和SSH的不同点,这也是企业常考初级程序员的面试题之一.说到这两套框架的不同,主要是持久层框架Hi ...
- 20175310 《Java程序设计》第8周学习总结
20175310 <Java程序设计>第8周学习总结 本周博客: https://www.cnblogs.com/xicyannn/p/10722004.html 教材学习内容总结 这周学 ...
- 关于NOIP2018复赛若干巧合的声明
关于NOIP2018复赛若干巧合的声明 导言 参加NOIP2018时本人学龄只有两个月,却斩获了省一等奖,保送了重点中学的重点班,这看上去是个我创造的神话,然而,在我自己心中,我认为这只是个巧合(其实 ...
- 用 Django 管理现有数据库
在多数项目中,总有一些几乎一成不变的 CRUD 操作,编写这些代码很无聊,但又是整个系统必不可少的功能之一.我们在上一个项目中也面临类似的问题,虽然已经实现了一个功能相对完整的管理后台,也尽量做到了代 ...
- 使用Sharding-Proxy进行分库分表
Sharding-Proxy的使用 1.官网下载 sharding-jdbc的官网http://shardingsphere.io/document/current/cn/manual/shardin ...
- SparkSQL – 从0到1认识Catalyst(转载)
最近想来,大数据相关技术与传统型数据库技术很多都是相互融合.互相借鉴的.传统型数据库强势在于其久经考验的SQL优化器经验,弱势在于分布式领域的高可用性.容错性.扩展性等,假以时日,让其经过一定的改造, ...