C++11 中的 emplace

27 篇文章 2 订阅

 


C++11大部分的容器对于添加元素除了传统的 insertpusb_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

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

First Snowflakes

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值