emplace_back和push_back在功能上是一致的,即在容器后方插入新的元素。那么相同逻辑的功能问什么要实现两边,以及为什么会有人推荐使用emplace_back代替push_back呢!
异同
相同点:
1. 支持传入右值 (push_back是c++11后才支持的)
不同点:
push_back:
a. 一定会发生拷贝构造
b. 不支持原地构造
c. 支持一个构造函数(此时其实也是发生了隐式构造)
emplace_back:
a. 不一定会发生拷贝构造
b. 支持原地构造
c. 支持多个构造函数
push_back
假设有一个std::vector<T> array,则array调用push_back时传入的变量只能是T类型的(当该类型构造函数只有一个参数时可以传入这个参数,发生隐式构造,实质上传入push_back的还是T类型的变量)。
细想一下这样的规则会发生什么,如果我们有一个如下类型的vector
class EXAMPLE
{
public:
EXAMPLE() = delete;
EXAMPLE(int a, int b)
: a_(a)
, b_(b)
{}
private:
int a_;
int b_;
}
int main()
{
std::vector<EXAMPLE> array;
array.push_back(1, 2); // 编译出错,push_back不支持原地构造
array.push_back({ 1, 2 }); // 编译成功
return 0;
}
上面代码中执行array.psuh_back({ a, b })首先会执行EXAMPLE的构造函数构造一个临时的EXAMPLE对象,接下来会调用拷贝构造函数将临时对象放到vector中。想一下若该类很大,执行拷贝构造效率很低,并且push_back被调用很多次,此时会造成很多性能的浪费。
emplace_back
若使用emplace_back便可以避免push_back带来的性能浪费。
int main()
{
std::vector<EXAMPLE> array;
array.emplace_back(1, 2); // 编译成功
array.emplace_back({ 1, 2 }); // 编译出错
array.emplace_back(EXAMPLE(1, 2)); // 编译成功
return 0;
}
array.emplace_back(1, 2)会执行原地构造,在emplace_back内部构造,不会发生拷贝构造。array.emplace_back(EXAMPLE(1, 2))会先构造一个临时的对象,再执行拷贝构造放入vector中。
总结
虽然在性能方面emplace_back优于push_back,但emplace_back也有一些缺点:
1. emplace_back一般发生错误都会在比较深的位置,而push_back则更容易定位问题
2. push_back是函数,可以支持隐式转换,而emplace_back本质上是模板,不支持隐式转换