使用自定义类作为unordered_map键的问题及构造函数相关疑问
一、为什么你的unordered_map配置无法运行?
你遇到的报错有两个核心原因:
1. 遍历时代码未指定完整的模板参数
你在遍历originalToCopy时写了:
for (unordered_map<RandomListNode, RandomListNode>::value_type value : originalToCopy) { ... }
这里的unordered_map<RandomListNode, RandomListNode>没有指定你自定义的MyHash和KeyEqual模板参数,编译器会尝试实例化默认的std::hash<RandomListNode>和std::equal_to<RandomListNode>——但标准库没有为自定义结构体提供默认哈希函数,因此触发报错。
你自己发现的修正写法是对的,但更简洁且不易出错的方式是用auto推导:
for (auto& value : originalToCopy) { ... }
这样编译器会自动匹配originalToCopy的完整模板类型,避免手动写错。
2. 错误地将RandomListNode对象作为键(而非指针)
这是更关键的问题:你的哈希函数MyHash用了&node(对象的内存地址),相等判断KeyEqual也依赖对象地址,但unordered_map存储键时会拷贝你的RandomListNode对象。这意味着:
- 存入map的键对象和你原始节点的地址完全不同,你的哈希/相等逻辑彻底失效;
- 即使你用原始节点去查询map,也会因为地址不匹配找不到对应条目。
针对复制带随机指针的链表场景,正确的做法是用指针作为键:
// 不需要自定义哈希和相等函数,标准库支持指针的默认哈希与比较 unordered_map<RandomListNode*, RandomListNode*> originalToCopy;
这样哈希函数默认使用指针的地址,相等判断默认比较指针值,完全符合你的需求(映射原始节点指针到拷贝节点指针)。
二、关于无默认/拷贝/移动构造函数的类的疑问
如果RandomListNode确实无法修改,且没有默认构造、拷贝构造或移动构造函数:
1. operator[]绝对无法使用
unordered_map::operator[]的核心逻辑是:当键不存在时,会默认构造一个键的实例并插入到map中,因此它要求键类型必须具备默认构造函数。如果你的类没有默认构造,调用operator[]会直接编译报错。
2. emplace()的可用性取决于具体情况
emplace()的作用是在map内部直接构造键值对,不需要先构造好对象再拷贝/移动进去,但它有前提:
- 你必须能传递足够的参数,让map在原地构造
pair<const Key, T>; - 如果键类型没有拷贝/移动构造函数,那么构造键时必须完全依赖原地构造(即通过完美转发参数调用键的构造函数)。
但回到你的场景,如果你坚持用RandomListNode对象作为键(不推荐),而该类只有带int的构造函数且无拷贝/移动构造,你可以尝试用piecewise_construct来原地构造键值对:
originalToCopy.emplace( std::piecewise_construct, std::forward_as_tuple(10), // 传递给RandomListNode构造函数的参数(键) std::forward_as_tuple(10) // 传递给RandomListNode构造函数的参数(值) );
但仍然要提醒:这种做法会导致键对象和原始节点地址不同,你的哈希/相等逻辑还是失效的,所以用指针作为键才是正确的选择。
内容的提问来源于stack exchange,提问作者pwh

