起因介绍
今天在力扣刷题时,我发现了一个问题,当我使用emplace_back时会出现报错,但是使用push_back时不会。
在我之前的学习中学到的是两者语义相同,而emplace_back比push_back的效率更高,尽可能使用emplace_back。所以当我碰到这样的问题就开始出现了疑问,便开始在网上寻找原因。
原因分析
vector中的emplace_back和push_back都是用来在数组尾部添加元素的
emplace_back与push_back区别
push_back和emplace_back的底层实现逻辑不一样:
push_back在向数组尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中,如果是拷贝的话之后需要进行销毁;而 emplace_back() 在实现向数组尾部添加元素时,是直接在容器尾部创建这个元素,从而省去了拷贝或移动元素。这样可以看出是emplace_back效率更高
源码分析
emplace_back源码
template <class... _Valty>
decltype(auto) emplace_back(_Valty&&... _Val) {
// insert by perfectly forwarding into element at end, provide strong guarantee
auto& _My_data = _Mypair._Myval2;
pointer& _Mylast = _My_data._Mylast;
if (_Mylast != _My_data._Myend) {
return _Emplace_back_with_unused_capacity(_STD forward<_Valty>(_Val)...);
}
_Ty& _Result = *_Emplace_reallocate(_Mylast, _STD forward<_Valty>(_Val)...);
#if _HAS_CXX17
return _Result;
#else // ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv
(void) _Result;
#endif // _HAS_CXX17
}
push_back源码
void push_back(const _Ty& _Val) { // insert element at end, provide strong guarantee
emplace_back(_Val);
}
void push_back(_Ty&& _Val) { // insert by moving into element at end, provide strong guarantee
emplace_back(_STD move(_Val));
}
原来,push_back是调用了emplace_back,这应该是c++11的源码,因为emplace_back是c++11新增的,所以以前的push_back应该不是这样的。
阅读源码我们可以发现:push_back底层调用的就是emplace_back,所以理论上应该是不会发生这样的错误。
之后,我又继续寻找原因,之后在看了多篇技术文章之后终于知道了原因,那就是类型推导的原因,push_back在传入参数时是已知的数据类型,而emplace_back则是能接受任何类型的数据,而如果我们没有规定数据类型,则会陷入类似互相推导的死循环。
加上类型后
果然,给emplace_back加上类型限制后就可以通过了。
收获
虽然emplace_back在性能上优于push_back,一般情况下我们都使用emplace_back,但是记得在使用emplace_back时加上显式类型限制。