C++中的map花括号初始化和unique_ptr

4
为什么我无法使用unique_ptr的花括号初始化方式来初始化map?
编辑:我使用c++17模式下的gcc 7.2。
我可以使用make_pair和[]运算符创建并插入unordered map。
std::unordered_map<std::string, std::unique_ptr<A>> map;

map.insert(std::make_pair("hello",std::make_unique<A>()));
map["foo"] = std::make_unique<A>();

但我不明白为什么使用大括号时会失败。
map.insert({"foo", std::make_unique<A>()}); // Error

错误:使用已删除的函数'std :: pair<_T1,_T2> :: pair(const std :: pair<_T1,_T2>&)。

1
你正在使用C++14,因为在此之前没有std::make_unique - Rakete1111
1
据我所知插入时会复制(unique_ptr是不可复制的),请使用emplace。 - Sopel
1个回答

3
自C++11开始,关联容器插入需要使用可复制构造的左值或可移动插入的可变右值。因此,与make_pair()一起调用insert()没有问题:后者构造了一个pair,用于初始化value_type rvalue。
使用大括号初始化的问题在于(直到C++17),value_type const&重载会被优先选择(因为没有合适的value_type&&重载),因此会请求进行复制并引发错误。
实际上,从C++11开始,相关的插入语义要求如下:
[unord.req] 如果t是非const右值表达式,则value_type必须可移动插入X;否则,value_type必须可复制插入X。
这是实际成员规范的区别所在;直到C++17,我们有:
pair<iterator, bool> insert(const value_type& obj);
template <class P> pair<iterator, bool> insert(P&& obj);
...

泛型重载是受 SFINAE 约束的,约束条件为 std::is_constructible<value_type, P&&>::value。很明显,使用 花括号初始化列表 将会选择 const& 重载(注意:还有一个 initializer_list 重载,但它不适用于 OP 的情况)。

相反,在 C++17 中我们也有

pair<iterator, bool> insert(value_type&& obj);

看起来gcc对c++17的支持仍然是实验性的(https://gcc.gnu.org/projects/cxx-status.html),这就是为什么它在我的电脑上无法编译(gcc 7.2)。非常好的解释。谢谢。 - Laurent Zubiaur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接