STL emplace_back

C++11起,STL部分容器增加emplace和emplace_back成员函数,如std::list和std::vector。以std::vector::emplace_back为例,C++17改变其返回值。emplace_back在容器位置直接构造元素,而push_back需拷贝或移动。支持emplace_back时,推荐用它代替push_back。

一 介绍

C++11开始,STL的一些容器中增加了emplace和emplace_back成员函数,例如 std::list 和 std::vector。

 以 std::vector::emplace_back举例:

template< class... Args >
void emplace_back( Args&&... args ); (C++11 起) (C++17 前)
template< class... Args >
reference emplace_back( Args&&... args ); (C++17 起)

从C++17开始,改变了返回值,返回被插入元素的引用。

emplace_back 添加新元素到容器尾。元素通过 std::allocator_traits::construct 构造,它典型地用布置 new 于容器所提供的位置原位构造元素。参数 args... 以 std::forward<Args>(args)... 转发到构造函数。

二 emplace_back 和 push_back

简单来说,emplace_back是在容器所提供的位置直接构造,而push_back添加一个新元素到vector末尾,然后拷贝或者移动到新元素。

#include <iostream>
#include <vector>

class A {
 public:
  A() {
    std::cout << "constructor" << std::endl;
  }
  A(int n) {
    std::cout << "constructor int" << std::endl;
  }
  A(const A& a) {
    std::cout << "copy constructor" << std::endl;
  }
  ~A() {
    std::cout << "destructor" << std::endl;
  }
};

int main() {
  A a;
  {
    std::vector<A> vc;
    vc.reserve(10); // 排除扩容干扰
    std::cout << std::endl;
    std::cout << "push_back:" << std::endl;
    vc.push_back(1);
    vc.push_back(a);
  }

  {
    std::vector<A> vc1;
    vc1.reserve(10);
    std::cout << std::endl;
    std::cout << "emplace_back:" << std::endl;
    vc1.emplace_back(1);
    vc1.emplace_back(a);
  }

  std::cin.get();
  return 0;
}

在push_back中先用参数1构造一个临时的A实例,然后拷贝到vector,最后析构临时的A实例;而emplace_back仅用参数1原位构造了一个A实例。对于插入a,表现一致,都是调用A的拷贝构造函数。

所以在支持emplace_back的场景下,推荐用emplace_back代替push_back。

三 补充(2020.10.16)

以 std::list push_back 为例,定义如下:

// std::list<T,Allocator>::push_back
void push_back( const T& value ); (1)	
void push_back( T&& value ); (2)(since C++11)

可以看出,C++11开始,push_back 也增加了移动元素的形式,也就是说:

std::vector<A> vc;

// 等同, 均调用A的移动构造函数
{
  A a;
  vc.push_back(std::move(a));
}
{
  A a;
  vc.emplace_back(std::move(a));
}

为了进一步说明,将二中的例子进行了修改,如下:

#include <iostream>
#include <vector>

class A {
 public:
  A() { std::cout << "constructor" << std::endl; }
  A(int n) { std::cout << "constructor int" << std::endl; }
  A(const A& a) { std::cout << "copy constructor" << std::endl; }
  A(A&& a) { std::cout << "move constructor" << std::endl; } // 增加移动构造函数
  ~A() { std::cout << "destructor" << std::endl; }
};

int main() {
  {
    std::vector<A> vc;
    vc.reserve(10);  // 排除扩容干扰
    std::cout << std::endl;
    std::cout << "push_back:" << std::endl;
    vc.push_back(1);
    {
      std::cout << "------------------------" << std::endl;
      A a;
      vc.push_back(a); 
    }
    {
      std::cout << "------------------------" << std::endl;
      A a;
      vc.push_back(std::move(a)); 
    }
  }

  {
    std::vector<A> vc1;
    vc1.reserve(10);
    std::cout << std::endl;
    std::cout << "emplace_back:" << std::endl;
    vc1.emplace_back(1);
    {
      std::cout << "------------------------" << std::endl;
      A a;
      vc1.emplace_back(a);
    }
    {
      std::cout << "------------------------" << std::endl;
      A a;
      vc1.emplace_back(std::move(a));
    }
  }

  std::cin.get();
  return 0;
}

结果如下:

push_back:
constructor int
move constructor  // 与上例不同,原因见下
destructor
------------------------
constructor
copy constructor   
destructor
------------------------
constructor
move constructor
destructor
destructor
destructor
destructor

 

emplace_back:
constructor int
------------------------
constructor
copy constructor
destructor
------------------------
constructor
move constructor
destructor
destructor
destructor
destructor

* 原因:在C++11中,拷贝构造/赋值 和 移动构造/赋值 必须同时提供或者同时不提供,才能保证类同时具有拷贝和移动语义,只声明一种的话,类仅能实现一种语义。参见 C++11 右值引用(2)移动构造和移动赋值函数

<think>我们正在讨论C++中的emplace_back函数。根据用户的问题,他们想了解emplace_back的功能和用法,并请求示例。 参考引用中提供了一个关于push_backemplace_back区别的示例,我们可以基于此进行解释。 首先,emplace_back是C++11引入的成员函数,用于在容器的末尾直接构造一个元素,而不是先构造一个临时对象再复制或移动到容器中。 这样可以避免不必要的复制或移动操作,提高效率。 与push_back的区别: - push_back:通常需要先构造一个临时对象,然后通过复制或移动构造函数将对象添加到容器末尾。 - emplace_back:直接在容器内使用给定的参数构造对象,避免了临时对象的创建。 因此,emplace_back在性能上通常更优,尤其是在对象较大或复制/移动成本较高时。 下面我们通过一个示例来说明emplace_back的用法,并对比push_back。 示例代码(基于引用中的示例,稍作调整以突出功能): ```cpp #include <iostream> #include <vector> class Vertex { public: Vertex(int x, int y, int z) : x(x), y(y), z(z) { std::cout << "Vertex constructor called with (" << x << ", " << y << ", " << z << ")" << std::endl; } // 复制构造函数 Vertex(const Vertex& other) : x(other.x), y(other.y), z(other.z) { std::cout << "Vertex copy constructor called" << std::endl; } // 移动构造函数 Vertex(Vertex&& other) noexcept : x(other.x), y(other.y), z(other.z) { std::cout << "Vertex move constructor called" << std::endl; } private: int x, y, z; }; int main() { std::vector<Vertex> vertices1; std::cout << "Using push_back:" << std::endl; // 使用push_back:先构造一个临时Vertex对象,然后移动(或复制)到vector中 vertices1.push_back(Vertex(1, 2, 3)); std::vector<Vertex> vertices2; std::cout << "\nUsing emplace_back:" << std::endl; // 使用emplace_back:直接在vector中构造Vertex对象,传入构造参数 vertices2.emplace_back(1, 2, 3); return 0; } ``` 运行此代码,输出可能如下: ``` Using push_back: Vertex constructor called with (1, 2, 3) // 在main函数中构造临时对象 Vertex move constructor called // 将临时对象移动到vector中(如果编译器优化可能省略移动,但此例中我们期望看到移动) Using emplace_back: Vertex constructor called with (1, 2, 3) // 直接在vector中构造,没有复制或移动 ``` 注意:实际输出可能因编译器优化(如复制省略)而有所不同。但在未优化的情况下,push_back会调用一次构造函数和一次移动构造函数(或复制构造函数,如果没有移动构造函数),而emplace_back只调用一次构造函数。 使用emplace_back时,我们传递的是构造函数的参数,而不是对象本身。emplace_back会使用这些参数在容器内直接构造对象。 注意事项: 1. 使用emplace_back时,参数必须与元素类型的某个构造函数匹配。 2. 对于内置类型(如int),push_backemplace_back性能相同。 3. 在需要向容器中添加临时对象时,使用emplace_back可以避免不必要的复制/移动,提高性能。 因此,emplace_back的主要功能是“就地构造”元素,从而优化性能。 相关问题: 1. emplace_back在什么情况下比push_back更高效? 2. 使用emplace_back时需要注意哪些问题? 3. 除了vector,其他STL容器是否也有emplace_back类似的函数?</think>### C++ `emplace_back` 函数功能及使用示例 `emplace_back` 是 C++11 引入的 STL 容器成员函数,主要用于在容器(如 `std::vector`)末尾**直接构造元素**,避免不必要的复制或移动操作,从而提升性能。 #### 核心功能 1. **原地构造** 直接使用参数在容器内存中构造对象,无需创建临时对象。 2. **避免拷贝/移动** 相比 `push_back`,省去了临时对象的构造和复制/移动开销。 3. **完美转发** 通过变长模板和完美转发(`std::forward`)保留参数类型信息。 #### 与 `push_back` 的对比 | 特性 | `push_back` | `emplace_back` | |--------------------|---------------------------------|----------------------------------| | **参数类型** | 对象本身(如 `Vertex(1,2,3)`) | 构造参数(如 `1,2,3`) | | **构造过程** | 先构造临时对象,再复制/移动 | 直接在容器内存中构造 | | **性能** | 可能触发复制/移动构造函数 | 通常只需一次构造函数调用 | | **适用场景** | 简单类型或已有对象 | 复杂对象或构造成本高的类型 | #### 使用示例 ```cpp #include <iostream> #include <vector> class Vertex { public: Vertex(int x, int y, int z) : x(x), y(y), z(z) { std::cout << "构造函数调用: (" << x << "," << y << "," << z << ")\n"; } Vertex(const Vertex& other) { std::cout << "拷贝构造函数调用\n"; } Vertex(Vertex&& other) noexcept { std::cout << "移动构造函数调用\n"; } private: int x, y, z; }; int main() { std::vector<Vertex> vec; // 使用 push_back(触发移动构造) std::cout << "--- push_back ---\n"; vec.push_back(Vertex(1, 2, 3)); // 先构造临时对象,再移动 // 使用 emplace_back(直接构造) std::cout << "\n--- emplace_back ---\n"; vec.emplace_back(4, 5, 6); // 直接在容器内构造 return 0; } ``` #### 输出结果 ``` --- push_back --- 构造函数调用: (1,2,3) // 临时对象构造 移动构造函数调用 // 移动到容器 --- emplace_back --- 构造函数调用: (4,5,6) // 直接构造,无额外操作 ``` #### 关键注意事项 1. **参数匹配** 传递的参数必须与类的构造函数严格匹配。 2. **显式构造** 避免 `vec.emplace_back({1,2,3})` 这类隐式转换,可能引发歧义。 3. **性能优势** 对复杂对象(如大尺寸类/不可复制对象)性能提升显著,简单类型(如 `int`)与 `push_back` 无差异。 4. **容器支持** 除 `vector` 外,`deque`/`list`/`forward_list` 等也支持 `emplace_back`。 #### 适用场景 - 构造成本高的对象(如包含大量资源的类) - 不可复制/移动的对象(需结合容器分配器) - 需要避免拷贝的敏感数据(如加密密钥) > 通过减少临时对象创建,`emplace_back` 在 C++11 及以上版本中成为高效容器操作的推荐选择[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值