美文网首页
emplace相关知识点总结

emplace相关知识点总结

作者: JamesBob | 来源:发表于2022-01-14 17:32 被阅读0次

1 emplace与insert的异同

1.1 相同点

将容器中插入新成员

emplace是C++11新标准引入了新成员,同时引入的 还有emplace_front、emplace_back。分别对应容器的原有操作insert、push_front、push_back。
其功能分别为:将元素插入到一个指定的位置、将元素插入到容器头部、将元素插入到容器尾部。

1.2 不同点

是否创建临时对象,触发拷贝动作

  • 调用push或者insert时,将元素类型的对象传递出去,这些对象被拷贝到容器当中,或者创建一个局部临时对象,并将其压入容器;
  • 调用emplace时,则是将参数传递给元素类型的构造函数,emplace成员使用这些参数在容器管理的内存空间中直接构造元素,没有拷贝操作。

1.3 示例说明

1.3.1 源码

#include <iostream>
#include <string>
#include <vector>

using namespace std;

//定义一个student类型
struct student {
  student() = default;
  student(const std::string &str, int num) : name_(str), num_(num) {
      cout << name_ << " constructor called" << endl;
  }
  student(student &stu) {
      this -> name_ = stu.name_;
      this -> num_ = stu.num_;
      cout << name_ << " copy constructor called" << endl;
  }
  student(student &&stu) {
      this -> name_ = stu.name_;
      this -> num_ = stu.num_;
      cout << name_ << " move constructor called" << endl;
  }
  ~student() {
      cout << name_ << " destructor constructor called" << endl;
  }
  std::string name_;
  int num_;
};

int main() {
  vector<student> stu_vec;
  stu_vec.reserve(10); // 不提前reserve会有多次拷贝操作
  cout << "==========emplace_back right val==========" << endl;
  stu_vec.emplace_back(student("lily", 3));
  cout << stu_vec.size() << endl;//size = 1
  stu_vec.emplace_back("bob", 1);//在stu_vec末尾构造一个student对象,使用2个参数的student构造函数
  cout << stu_vec.size() << endl;//size = 2

  cout << "==========push_back right val=========" << endl;
  stu_vec.push_back(student("tom", 2));//正确,创建一个临时的student对象,传递给push_back
  cout << stu_vec.size() << endl;//size = 3
//   stu_vec.push_back("tom", 2);//错误,没有接受2个参数的push_back版本,push不支持直接构造

  cout << "==========emplace_back left val==========" << endl;
  student stu1("mike", 4);
  stu_vec.emplace_back(stu1);
  cout << stu_vec.size() << endl;//size = 4

  cout << "==========push_back left val==========" << endl;
  student stu2("jeck", 5);
  stu_vec.emplace_back(stu2);
  cout << stu_vec.size() << endl;//size = 5
  return 0;
}

1.3.2 输出

image.png

1.3.3结论

1)emplace和push的对象是右值

  • emplace函数在容器中直接构造,push不支持直接构造, push则是先构造一个临时对象,再把该对象拷贝到容器中,临时对象还需要析构;
  • 针对构造好的右值,emplace和push没有区别;
  • 所有针对右值,emplace的效率更高。

2)emplace和push的对象是左值

  • emplace是直接把构造好的左值对象拷贝到容器当中
  • push也是直接把构造好的左值对象拷贝到容器当中
  • 所以针对左值,emplace和push的效率是一样的

1.3.4 左值右值

在C++中,一个左值是指向一个指定内存的东西。另一方面,右值就是不指向任何地方的东西。通常来说,右值是暂时和短命的,而左值则活的很久,因为他们以变量的形式(variable)存在。我们可以将左值看作为容器(container)而将右值看做容器中的事物。

2 std::piecewise_construct_t

2.1 简述

c++11
struct piecewise_construct_t { };   
c++14
struct piecewise_construct_t { explicit piecewise_construct_t() = default; }; 
  • std::piecewise_construct_t 是一个空的struct标记类型,用于区分带有两个元组参数的不同函数, 是接收两个 tuple 参数的不同函数间消歧义的空类标签类型。。
  • 不使用 std::piecewise_construct_t 的重载假定每个元组参数成为一对的元素。使用 std::piecewise_construct_t 的重载假定每个元组参数用于逐段构造指定类型的新对象,该对象将成为该pair的元素。

2.2 std::pair

  • std::pair有一个特别的构造函数,第一个参数类型为std::piecewise_construct_t, 实际就是一个空结构体类型,用作标识。
  • std::piecewise_construct_t作用就是其字面意思:分段构造。具体来说,因pair的key和value均可以是构造函数复杂类型,因而pair的初始化相对复杂,通过带有std::piecewise_construct_t类型参数,后跟两个tuple类型参数的构造函数,用第一个tuple类型的元素来构造pair的key, 用第二个tuple类型的元素来构造pair的value,从而实现pair的初始化。
  • forward_as_tuple经常与std::piecewise_construct_t配合使用
    pair构造函数

示例:

#include <iostream>
#include <utility>
#include <tuple>
 
struct Foo {
    Foo(std::tuple<int, float>) 
    {
        std::cout << "Constructed a Foo from a tuple\n";
    }
    Foo(int, float) 
    {
        std::cout << "Constructed a Foo from an int and a float\n";
    }
};
 
int main()
{
    std::tuple<int, float> t(1, 3.14);
    std::pair<Foo, Foo> p1(t, t);
    std::pair<Foo, Foo> p2(std::piecewise_construct, t, t);
}

输出:

Constructed a Foo from a tuple
Constructed a Foo from a tuple
Constructed a Foo from an int and a float
Constructed a Foo from an int and a float

2.3 std::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;
emplace方式:scp.emplace(piecewise_construct,forward_as_tuple("hello"),forward_as_tuple(1, 2));
insert方式:scp.insert({"world", {1, 2}});
  • 对于emplace方式来说,虽然避免了临时变量的构造,但是却需要构建两个 tuple 。
  • 从方便性和代码优雅性上来说,insert方式要胜过 emplace方式。
  • 因此对于临时变量构建代价不是很大的对象(比如基础类型)推荐使用 insert 而不是 emplace。

参考:
https://www.jianshu.com/p/94b0221f64a5
https://blog.csdn.net/wangmj_hdu/article/details/119537411
https://blog.csdn.net/luoshabugui/article/details/118696418
https://www.cnblogs.com/guxuanqing/p/11396511.html

相关文章

  • emplace相关知识点总结

    1 emplace与insert的异同 1.1 相同点 将容器中插入新成员 emplace是C++11新标准引入了...

  • GeekBand - iOS开发实战第三周笔记

    本周主要总结一下 autolayout 相关的知识点。本来打算把CollectionView的相关知识点总结一下...

  • 018 使用 emplace 操作

    C++ 11 引入了三个新成员 —— emplace_front、emplace 和 emplace_back,这...

  • 2019-05-04

    文综 1.做题→相关知识点。 2.画框架→勾连相关知识点→做相关题 语文: 听课→笔记→输出 做题→总结→输出 英...

  • iOS知识体系总结-网络相关

    注意:更新内容会同步到GitHub iOSWiki-知识体系总结 总结-网络相关 iOS知识点/网络相关 #iOS...

  • iOS知识体系总结-内存相关

    注意:更新内容会同步到GitHub iOSWiki-知识体系总结 总结-内存相关 iOS知识点/内存相关 #iOS...

  • emplace/emplace_back

    emplace_back/emplace等能通过参数直接在容器中构造对象,相比push_back/insert能更...

  • SpringBoot使用总结

    以下是SpringBoot的SpringApplication相关知识点总结 1、SpringBoot IOC容器...

  • Replugin源码解析之replugin-host-libra

    概述 本文相关系统知识点在 上文 系统ClassLoader相关及Application初始化简单分析及总结 中,...

  • iOS Runloop

    这里记录下iOS中Runloop相关的知识点,以备以后复习总结。 先来说下Runloop相关的概念: Runloo...

网友评论

      本文标题:emplace相关知识点总结

      本文链接:https://www.haomeiwen.com/subject/gpbacrtx.html