Loading

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

posted @ 2022-05-09 20:46  azureology  阅读(896)  评论(0编辑  收藏  举报