以c++11标准的std::vector
为例,直接上源码:
// push_back
// 接收左值的重载版本
void push_back(const value_type& __x) {
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) {
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x);
++this->_M_impl._M_finish;
} else
_M_realloc_insert(end(), __x);
}
// 接收右值的重载版本
void push_back(value_type&& __x) { emplace_back(std::move(__x)); }
// emplace_back
// 万能引用,既接收左值又接收右值的唯一版本
template <typename _Tp, typename _Alloc>
template <typename... _Args>
void vector<_Tp, _Alloc>::emplace_back(_Args&&... __args) {
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) {
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
std::forward<_Args>(__args)...);
++this->_M_impl._M_finish;
} else
_M_realloc_insert(end(), std::forward<_Args>(__args)...);
}
比较源码得出结论:
push_back
有两个重载版本,分别是接收左值的版本,和接收右值的版本。emplace_back
只有一个万能引用的版本,既能接收左值又能接收右值。push_back
接收右值的版本其实就是调用的emplace_back
。c++11后引入了右值引用和移动语义等概念,通过移动提高了代码效率。其实在c++11之前,push_back
只有接收左值的版本,且没有emplace_back
,但这个接收左值的版本因为是常量引用(const T&),所以也可以接收右值,但会多一次临时变量的拷贝构造,相比移动要低效些。所以在c++11后,push_back
多了一个由emplace_back
代理实现的右值引用的版本,提高效率,且客户端不用修改代码,只需重新编译一遍,就能够悄无声息地享受到此移动操作带来的效率提高。push_back
的左值版本实现和emplace_back
实现差异不大,区别主要在于emplace_back
用了万能引用、可变参数模板、完美转发等特性,实现了对push_back
左值版本的扩展。所以emplace_back
可以接收多个参数实现就地构造,且总是能匹配到效率最高且正确的版本。但push_back
只能接受一个参数,就是该容器的元素,然后匹配效率最高的版本,但无法通过参数就地构造。
可参考之前博文:一文读懂emplace_back和push_back的最主要区别