C++11
中大部分的容器对于添加元素除了传统的 insert
或pusb_back/push_front
之外都提供一个新的函数叫做emplace
。 比如,如果你想要向 std::vector
的末尾添加一个数据,你可以:
std::vector<int> nums;
nums.push_back(1);
你也可以使用:
std::vector<int> nums; nums.emplace_back(1);
避免不必要的临时对象的产生
emplace
最大的一个作用是避免产生不必要的临时变量,因为它可以完成 in place
的构造,可以用以下的例子进行理解:
struct Foo {
Foo() {
cout << "Foo() is called" << endl;
mA = 0;
mB = 0;
}
Foo(int a) :mA(a), mB(0) {
cout << "Foo(int a) is called" << endl;
}
explicit Foo(int a,double b):mA(a),mB(b) {//该构造函数申明为explicit,禁止隐式类型转换
cout << "Foo(int a,double b) is called" << endl;
}
Foo(const Foo& obj) {
cout << "Foo(const Foo& obj) is called" << endl;
mA = obj.mA;
mB = obj.mB;
}
~Foo() {
cout << "~Foo() is called" << endl;
}
private:
int mA;
double mB;
};
示例1:
int main() {
vector<Foo> vec;
vec.push_back(Foo(1,2.0));
return 1;
}
output:
Foo(int a,double b) is called
Foo(const Foo& obj) is called
~Foo() is called
~Foo() is called
可见, 生成了一个临时对象,然后该临时对象用于去初始化vector内的那个Foo对象。
示例2:
int main() {
vector<Foo> vec;
vec.emplace_back((Foo(1, 2.0)));
return 1;
}
output:
Foo(int a,double b) is called
Foo(const Foo& obj) is called
~Foo() is called
~Foo() is called
可见, 同上,仍然生成了一个临时对象 。emplace_back发挥作用的,需要用示例3中的传参方式。
示例3: --- 避免了临时对象的产生。
int main() {
vector<Foo> vec;
vec.emplace_back(1, 2.0); //不要写成vec.emplace_back({ 1, 2.0 }); 这样编译不过
return 1;
}
output:
Foo(int a,double b) is called
~Foo() is called
emplace_back函数的两个参数自动用来构造 vector
内部的 Foo
对象。做到这一点主要 使用了 C++11
的两个新特性 变参模板
和 完美转发
。
”变参模板”使得 emplace
可以接受任意参数,这样就可以适用于任意对象的构建。
”完美转发”使得接收下来的参数 能够原样的传递给对象的构造函数,这带来另一个方便性就是即使是构造函数声明为 explicit
它还是可以正常工作,因为它不存在隐式转换。
Foo类的构造函数Foo(int a,double b)被声明为explicit, 用来禁止隐式转换,故,vec.push_back({ 1,2 })不能编译通过,除非将explicit的声明去掉。
顺带看 移动构造函数 和右值
以上的例子中我并没有定义 移动构造函数,看来emplace的效率高在于避免了临时对象的产生,跟 右值 和 移动构造函数 没有关系呀, 验证下:
在上面的Foo类中增加一个移动构造函数:
struct Foo {
....
Foo(const Foo&& obj) {
cout << "Foo(const Foo&& obj) is called" << endl;
mA = obj.mA;
mB = obj.mB;
}
.....
};
然后,
int main() {
vector<Foo> vec;
Foo obj(1, 2.0);
vec.push_back(move(obj));return 1;
}
输出:
Foo(int a,double b) is called
Foo(const Foo&& obj) is called
~Foo() is called
~Foo() is called
可见,在生成vector内部的那个Foo对象时,调用的为Foo的移动构造函数, 去掉move则调用的时复制构造函数。
Note:
vector<Foo> vec;
vec.push_back({ 1,2 }); 的输出与上面完全相同,都是先生成一个临时对象,然后去调用Foo的移动构造函数。
Ref:
http://blog.guorongfei.com/2016/03/16/cppx-stdlib-empalce/
https://blog.csdn.net/windpenguin/article/details/75581552