本周小贴士#112:emplace与push_back

作为totW/112最初发表于2016年2月25日

由Geoff Romer(gromer@google.com)创作

修订于2017年8月30日

“我们越少使用我们的力量,它就会越强大。”——托马斯.杰斐逊

正如你所知(如果不知道,参见TotW 65),C++11引入了一种强大的新方法来将条目插入到容器中:emplace方法。这些可以让使用任何对象的构建函数在一个容器内原地构建一个对象。这包括移动和拷贝构造函数,因此在你可以使用push或insert方法时,你可以使用emplace方法代替,而不需要其他修改。

std::vector<string> my_vec;
my_vec.push_back("foo");     // 这是可行的,因此...
my_vec.emplace_back("foo");  // 这也是可行的,并且有相同的结果
std::set<string> my_set;
my_set.insert("foo");        // 此处相同:任何调用imsert可以被emplace重写
my_set.emplace("foo");       

这产生一个明显的问题:你应该用哪一个?我们是否应该放弃push_back()和insert(),并且一直使用emplace方法吗?

让我通过问另外一个问题来回答这个问题:这两行代码做什么了?

vec1.push_back(1<<20);
vec2.emplace_back(1<<20);

第一行相当简单:它将数字1048576添加到vector的末尾。然而,第二行就不那么清晰了。在不知道vector类型的情况下,我们不知道它会调用什么构造函数,因此我们无法真正说出那行代码做了什么。例如,如果vec2是一个std::vector,那么那行仅仅添加1048576到末尾,就像第一行一样,但是如果vec2是std::vector<std::vector>,那么那第二行会构造一个超过百万个元素的vector,在该进程中分配几兆字节的内存。

因此,如果你在有相同的参数时,在push_back和emplace_back之间进行选择,如果你选择push_back,那么你的代码会更具有可读性,因为push_back更确切地表现了你的意图。选择push_back也更加安全:假定你有一个std::vector<std::vector>,并且你想附加一个数据到第一个vector,但是你偶然忘记了下标。如果你写my_vec.push_back(2<<20),那么你会得到一个编译错误,然后你会快速发现问题。另一方面,如果你编写my_vec.emplace_back(2<<2),代码将成功编译,并且直到运行前,你也不会注意到任何问题。

现在,当涉及隐式转换时,emplace_back()确实比push_back()快一些。例如,在我们要开始的代码中,my_vec.push_back(“foo”)从一个字符串字面量构造一个临时string,然后将string移动进容器中,然而emplace_back(“foo”)则直接在容器中构造string,避免了额外的移动。对于消耗更大的类型,这可能是用emplace_back()代替push_back()的一个原因,尽管存在可读性和安全性上的代价,但是情况可能并非如此。通常性能差异并不那么重要。与往常一样,经验法则是你应该避免对代码进行不够安全和不够清晰的“优化”,除非性能优势大到需要在你的应用程序基准测试中显示出来。

所以一般来说,如果push_back()和emplace_back()都使用相同的参数,那么你应该优先选择push_back(),对于insert()和emplace()也是如此。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值