emplace_back无法支持<brace-enclosed initializer list>吗?
问题
vector
使用emplace_back()
无法添加元素,使用push_back()
却可以。
难道emplace_back()
与列表初始化不兼容?非也!
复现
代码片段
std::vector<std::array<int, 4> > vec;
vec.emplace_back({1,2,3,4}); // error
vec.push_back({1,2,3,4}); // pass
报错信息
<source>:8:31: error: no matching function for call to 'std::vector<std::array<int, 4> >::emplace_back(<brace-enclosed initializer list>)'
8 | vec.emplace_back({1,2,3,4});
| ^
include/c++/11/bits/stl_vector.h:1212:2: note: candidate template ignored: substitution failure: deduced incomplete pack <(no value)> for template parameter '_Args'
emplace_back(_Args&&... __args);
^
深究
分析std::vector
的成员函数:
文档指出emplace_back()
本质上是一个模板函数,具体形式为
template< class... Args >
void emplace_back( Args&&... args );
对于简单的结构我们一般省略模板参数让编译器自行推导。对于复杂类型,
模板意味若不确定传入args
类型,编译器无法推导缺省模板参数Args
的值,
进而无法使用std::forward<Args>(args)...
转发参数进行构造。
说的好,但还是没有解释为什么push_back()
可以调用?
从实现上push_back()
并非模板函数,且其实现存在拷贝而非直接构造
void push_back( const T& value );
编译器将隐式调用std::array::operator=()
处理传入的参数完成类型转换。
解决
只需将上述代码改为:
vec.emplace_back<std::array<int, 4>>({1,2,3,4});
// or
vec.emplace_back(std::array<int, 4>{{1, 2, 3, 4}});
即可通过编译!
参考
std::vector<T,Allocator>::emplace_back - cppreference.com
std::vector<T,Allocator>::push_back - cppreference.com