std::unordered_map operator[] 对于不存在的键进行零初始化吗?

33
根据cppreference.com,对于不存在的值,std::map::operator[]执行零初始化。
然而,相同的网站没有提及std::unordered_map::operator[]的零初始化,除了它有一个依赖于此的示例。
当然,这只是一个参考网站,而不是标准。所以,下面的代码是否正确?
#include <unordered_map>
int main() {
    std::unordered_map<int, int> map;
    return map[42];     // is this guaranteed to return 0?
}

13
@Ælex 你不能可靠地测试某个东西是否已初始化。 - 463035818_is_not_a_number
2
@Ælex 我不是很明白,你怎么可能有一个未初始化的 std::optional - 463035818_is_not_a_number
2
@Ælex 没有办法测试对象是否初始化,因为除初始化之外的任何未初始化对象操作都会导致未定义行为。不包含任何值的 std::optional 对象仍然是已初始化的对象。 - bolov
2
值对象是值初始化的,而不是零初始化的。对于标量类型来说,它们是相同的,但对于类类型来说则不同。 - aschepler
@bolov,昨天我尝试使用 GNU 17 和 std 17 进行测试,但奇怪的是我只得到了零初始化。我以为 std::optionalhas_value 函数可以测试它,但失败了,所以我想你是正确的。 - Ælex
显示剩余2条评论
2个回答

24
在您提供的网站上,它说:
当使用默认分配器时,这将导致从键中复制构造,并使映射值初始化为默认值。
因此,int被初始化为默认值:
值初始化的效果是:
[...]
4)否则,对象将被零初始化
这就是为什么结果是0的原因。

19

根据要翻译的重载版本,std::unordered_map::operator[]等同于[unord.map.elem]

T& operator[](const key_type& k)
{
    return try_­emplace(k).first->second;
}

(接受右值引用的重载只是将k移动到try_emplace中,并且在其他方面完全相同)

如果在映射中存在键为k的元素,则try_emplace返回指向该元素和false的迭代器。 否则,try_emplace会在键k下插入一个新元素,并返回指向该元素和true的迭代器[unord.map.modifiers]:

template <class... Args>
pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);

我们感兴趣的是目前没有任何元素的情况 [unord.map.modifiers]/6

否则会插入一个使用 piecewise_­construct,forward_­as_­tuple(k),forward_­as_­tuple(std​::​forward<Args>(args)...) 构造的 value_­type 类型的对象

(获取右值引用的重载只是将 k 移动到 forward_­as_­tuple 中,并且在其他方面完全相同)

由于 value_typepair<const Key, T> 类型的 [unord.map.overview]/2,这告诉我们新的 map 元素将被构造为:

pair<const Key, T>(piecewise_­construct, forward_­as_­tuple(k), forward_­as_­tuple(std​::​forward<Args>(args)...));
args为空时,这意味着我们的新值将被构造为pair的成员,从无参 [pairs.pair]/14 直接初始化[class.base.init]/7,使用()作为初始化程序,这归结为值初始化 [dcl.init]/17.4。对于int的值初始化是零初始化[dcl.init]/8,而int的零初始化自然地将该int初始化为0 [dcl.init]/6

因此,是的,您的代码保证返回0...


如果可以的话,我会点赞两次的,太好了(而且友好)。谢谢。 - pdm

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