c++ push_back()和emplace_back()区别
c++ push_back()和emplace_back()区别
References
一、源码分析
(1)push_back()定义
// stl_vector.h
template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class vector : protected _Vector_base<_Tp, _Alloc>
{
...
void
push_back(const value_type& __x)
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_GLIBCXX_ASAN_ANNOTATE_GROW(1);
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
__x);
++this->_M_impl._M_finish;
_GLIBCXX_ASAN_ANNOTATE_GREW(1);
}
else
_M_realloc_insert(end(), __x);
}
#if __cplusplus >= 201103L
void
push_back(value_type&& __x)
{ emplace_back(std::move(__x)); }
}
- 需传入对应类型的对象(隐式构造的情况除外)
- C++11以及之后版本,传入右值需调用移动构造函数(若未定义,则调用拷贝构造函数)。
类的构造函数未添加
explicit
且只接受一个参数的情况,也可以传入单个参数进行构造。但涉及另一知识点:如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,有时我们把这种构造函数称作转换构造函数(converting constructor)。
- push_back传入参数不涉及类型推断,以下x为rvalue reference。
若没有一个特定的std::vector
template <class T, class Allocator = allocator<T> >
class vector {
public:
...
void push_back(T&& x); // fully specified parameter type => no type deduction;
... // && is rvalue reference
};
(2)emplace_back()定义
#if __cplusplus >= 201103L
template<typename _Tp, typename _Alloc>
template<typename... _Args>
#if __cplusplus > 201402L
typename vector<_Tp, _Alloc>::reference
#else
void
#endif
vector<_Tp, _Alloc>::
emplace_back(_Args&&... __args)
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_GLIBCXX_ASAN_ANNOTATE_GROW(1);
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
std::forward<_Args>(__args)...);
++this->_M_impl._M_finish;
_GLIBCXX_ASAN_ANNOTATE_GREW(1);
}
else
_M_realloc_insert(end(), std::forward<_Args>(__args)...);
#if __cplusplus > 201402L
return back();
#endif
}
#endif
- emplace_back既可以传入相应类型对象,还可以传入元素的类构造函数的参数列表进行原地构造。参数列表通过std::forward进行完美转发。
- emplace_back的传入参数需要类型推断,以下Args为Universal Reference.
emplace_back
类型参数Args独立于vector模板类型参数T,所以Args类型在每次被调用的时候都必须被推断。
template <class T, class Allocator = allocator<T> >
class vector {
public:
...
template <class... Args>
void emplace_back(Args&&... args); // deduced parameter types => type deduction;
... // && is universal references
};
二、对比push_back和emplace_back
(1)定义测试类
class Foo{
private:
int a;
public:
Foo(int a): a(a){
cout<<"Foo Constructor "<<a<<endl;
}
Foo(){
cout<<"Default Foo Constructor"<<endl;
}
~Foo() { cout << "Foo Destructor..." <<a<<endl; }
Foo(const Foo& foo){
cout<<"copy constructor"<<endl;
}
Foo(Foo&& foo){
cout<<"move constructor"<<endl;
}
};
(2)测试用例
- push_back拷贝构造,emplace_back原地构造.
#include <iostream>
#include <vector>
using namespace std;
int main() {
cout<<endl<<"push_back begin"<<endl<<endl;
vector<Foo> vec1;
Foo foo1(1);
vec1.push_back(foo1);
cout<<endl<<"push_back end"<<endl<<endl;
cout<<endl<<"emplace_back begin"<<endl<<endl;
vector<Foo> vec2;
vec2.emplace_back(2);
cout<<endl<<"emplace_back end"<<endl<<endl;
}
输出:
push_back begin
Foo Constructor 1
copy constructor //调用拷贝构造
push_back end
emplace_back begin
Foo Constructor 2 //vector上原地构造,无需拷贝,也无需移动
emplace_back end
Foo Destructor...2
Foo Destructor...1
Foo Destructor...0
- 拷贝构造函数和移动构造函数均已定义,用push_back/emplace_back向容器中加入左值元素,均将调用拷贝构造函数拷贝对象到容器中。
#include <iostream>
#include <vector>
using namespace std;
int main() {
cout<<endl<<"push_back begin"<<endl<<endl;
vector<Foo> vec1;
Foo foo1(1);
vec1.push_back(foo1);
cout<<endl<<"push_back end"<<endl<<endl;
cout<<endl<<"emplace_back begin"<<endl<<endl;
vector<Foo> vec2;
Foo foo2(2);
vec2.emplace_back(foo2);
cout<<endl<<"emplace_back end"<<endl<<endl;
}
输出:
push_back begin
Foo Constructor 1
copy constructor
push_back end
emplace_back begin
Foo Constructor 2
copy constructor
emplace_back end
Foo Destructor...2 //左值离开作用域销毁
Foo Destructor...0
Foo Destructor...1 //左值离开作用域销毁
Foo Destructor...0
- 拷贝构造函数和移动构造函数均定义,push_back/emplace_back向容器中加入右值元素, 那么均会调用移动构造函数在vector上进行构造。
- 加入显式构造的右值元素
vec1.push_back(Foo(1));
vec1.emplace_back(Foo(1));
- 通过std::move()转化而成的右值元素
Foo foo1;
vec1.push_back(std::move(foo1))
Foo foo2;
vec1.emplace_back(std::move(foo2));
- 测试
#include <iostream>
#include <vector>
using namespace std;
int main() {
cout<<endl<<"push_back begin"<<endl<<endl;
vector<Foo> vec1;
vec1.push_back(Foo(1));
cout<<endl<<"push_back end"<<endl<<endl;
cout<<endl<<"emplace_back begin"<<endl<<endl;
vector<Foo> vec2;
vec2.emplace_back(Foo(2));
cout<<endl<<"emplace_back end"<<endl<<endl;
}
输出:
push_back begin
Foo Constructor 1
move constructor
Foo Destructor...1 //右值立马销毁
push_back end
emplace_back begin
Foo Constructor 2
move constructor
Foo Destructor...2 //右值立马销毁
emplace_back end
Foo Destructor...0
Foo Destructor...0
- 若仅定义拷贝构造函数,未定义移动构造函数,push_back/emplace_back向容器中加入右值元素, 那么均会调用拷贝构造函数构造对象在vector上。
#include <iostream>
#include <vector>
using namespace std;
class Foo{
private:
int a;
public:
Foo(int a): a(a){
cout<<"Foo Constructor "<<a<<endl;
}
Foo(){
cout<<"Default Foo Constructor"<<endl;
}
~Foo() { cout << "Foo Destructor..." <<a<<endl; }
Foo(const Foo& foo){
cout<<"copy constructor"<<endl;
}
// Foo(const Foo&& foo){
// cout<<"move constructor"<<endl;
// }
// Foo(Foo&& foo)=delete;
};
int main() {
cout<<endl<<"push_back begin"<<endl<<endl;
vector<Foo> vec1;
vec1.push_back(Foo(1));
cout<<endl<<"push_back end"<<endl<<endl;
cout<<endl<<"emplace_back begin"<<endl<<endl;
vector<Foo> vec2;
vec2.emplace_back(Foo(2));
cout<<endl<<"emplace_back end"<<endl<<endl;
}
输出:
push_back begin
Foo Constructor 1
copy constructor
Foo Destructor...1 //右值立马销毁
push_back end
emplace_back begin
Foo Constructor 2
copy constructor
Foo Destructor...2 //右值立马销毁
emplace_back end
Foo Destructor...0
Foo Destructor...0