C 标准委员会的目的是在C 11 Unordered_map中破坏其所插入的内容
Is it intended by the C++ standards committee that in C++11 unordered_map destroys what it inserts?
我刚刚损失了三天的时间,跟踪一个非常奇怪的错误,其中unordered_map :: insert()破坏了您插入的变量。这种高度不太明显的行为仅在最近的编译器中发生:我发现Clang 3.2-3.4和GCC 4.8是唯一的编译器来演示此"功能"。
以下是我的主要代码库中的一些简化代码,其中证明了问题:
#include <memory>
#include <unordered_map>
#include <iostream>
int main(void)
{
std::unordered_map<int, std::shared_ptr<int>> map;
auto a(std::make_pair(5, std::make_shared<int>(5)));
std::cout << "a.second is " << a.second.get() << std::endl;
map.insert(a); // Note we are NOT doing insert(std::move(a))
std::cout << "a.second is now " << a.second.get() << std::endl;
return 0;
}
i,可能像大多数C 程序员一样,希望输出看起来像这样:
a.second is 0x8c14048
a.second is now 0x8c14048
但是,有了叮当响3.2-3.4和GCC 4.8,我得到了:
a.second is 0xe03088
a.second is now 0
这可能没有意义,直到您仔细检查http://www.cplusplus.com/reference/reference/unordered_map/unordered_map/insert/proceproad no no 2 is
template <class P> pair<iterator,bool> insert ( P&& val );
这是一种贪婪的通用参考移动过载,消耗与其他任何过载不匹配的任何东西,以及移动构造 中的value_type。那么,为什么我们上面的代码选择此过载,而不是大多数人期望的unordered_map :: value_type超载?
答案凝视着您:unordered_map :: value_type是一对&lt; em const int,std :: sharde_ptr>,编译器会正确认为一对&lt; int; int ,std :: shared_ptr>不转换。因此,编译器选择了Move Universal Reference Overload,并且尽管程序员不使用std :: move(),但它会破坏原始的,这是指示您可以随着变量被销毁的典型约定。因此,按照C 11标准,插入破坏行为实际上是正确的,而较旧的编译器不正确。。
您现在可能可以看到为什么我花了三天的时间诊断此错误。在大型代码库中,插入unordered_map的类型在源代码术语中定义的typedef中根本不明显,任何人从来没有想到typedef与value_type相同。
。所以我要堆叠溢出的问题:
为什么较老的编译器不会破坏像较新的编译器一样插入的变量?我的意思是,即使是GCC 4.7也不这样做,这是相当标准的。
这个问题是否广为人知,因为升级编译器肯定会导致代码突然停止工作?
C 标准委员会是否打算这种行为?
您如何建议修改unordered_map :: insert()以提供更好的行为?我之所以问这个,是因为如果这里有支持,我打算向WG21提交此行为,并要求他们实施更好的行为。
正如其他人在评论中指出的那样,"通用"构造函数实际上并非总是从其论点中移动。如果该论点确实是一个rvalue,它应该移动,如果它是lvalue,请复制。
您观察到的行为总是移动的,是libstdc 的一个错误,现在根据对问题的评论修复了该行为。对于那些好奇的人,我看了看G -4.8标头。
bits/stl_map.h
,第598-603行
template<typename _Pair, typename = typename
std::enable_if<std::is_constructible<value_type,
_Pair&&>::value>::type>
std::pair<iterator, bool>
insert(_Pair&& __x)
{ return _M_t._M_insert_unique(std::forward<_Pair>(__x)); }
bits/unordered_map.h
,行365-370
template<typename _Pair, typename = typename
std::enable_if<std::is_constructible<value_type,
_Pair&&>::value>::type>
std::pair<iterator, bool>
insert(_Pair&& __x)
{ return _M_h.insert(std::move(__x)); }
后者使用std::move
错误地使用std::forward
。
template <class P> pair<iterator,bool> insert ( P&& val );
这是一种贪婪的通用参考移动过载,消耗了与其他任何过载不匹配的任何东西,然后将其构造为value_type。
这就是某些人所说的通用参考,但实际上是参考崩溃。在您的情况下, pair<int,shared_ptr<int>>
类型的参数为 lvalue ,它将导致参数为rvalue参考,并且它不应移动从中。
那么为什么我们的代码选择此超载,而不是 unordered_map :: value_type超负荷,大多数人可能期望?
因为您和其他人一样,误解了容器中的value_type
。*map
的value_type
(无论是订购还是无序)是pair<const K, T>
,在您的情况下是pair<const int, shared_ptr<int>>
。不匹配的类型消除了您可能期望的过载:
iterator insert(const_iterator hint, const value_type& obj);
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 预处理器:插入结构名称中的前一个行号
- 在未初始化映射的情况下,将值插入到映射的映射中
- 如何在c++中只将键插入到bimap的一侧
- 如何将结构插入到集合中并打印集合的成员
- C++json插入数组
- Visual Studio 2019:插入多个C++风格的单行注释
- nlohmann-json将一个数组插入到另一个数组中
- 有效地使用std::unordered_map来插入或增加键的值
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- 正在插入动态数组
- 插入或删除时获取usb的dos_name
- 叮叮当当在修复时插入多个"覆盖"说明符
- 链表c++插入,所有情况都已检查,但没有任何工作
- 将重物插入std::map
- C++17 - 使用自定义分配器的节点提取/重新插入 - 适用于 clang++/libc++,但不适用于 libstd
- 在数字之间插入 + 或 - 符号以使其等于整数
- 海湾合作委员会 ARM 性能下降
- C 标准委员会的目的是在C 11 Unordered_map中破坏其所插入的内容