静态std::unordered_map的默认值

15

我想了解静态结构体内static std::unordered_map<std::string, struct>变量的默认值。

以下是我的示例代码:

#include <iostream>
#include <string>
#include <unordered_map>

int main()
{
    enum MyStateType
    {
        MY_STATE_NEW,
        MY_STATE_RUN,
        MY_STATE_FREE
    };
    struct Foo
    {
        int num;
        MyStateType state;
    };
    static std::unordered_map<std::string, Foo> map;

    std::cout << map["Apple"].num << '\n';
    std::cout << map["Apple"].state << '\n';
}

输出结果:

0
0
Program ended with exit code: 0

可以认为在 Foo 内部的变量一开始总是被初始化为 0 吗?


6
不要在 STL map 容器中使用 operator[]。如果键不存在,它们会构造该值。应改用 .at() - Henri Menke
5
在这种情况下,使用 std::mapstd::unordered_map 的数组索引 operator[] 添加新项是安全的,并且事实上是标准规定的。这些新项将通过 值初始化 的方式添加。除了用户定义构造函数(在其中您需要负责成员初始化)之外,像您这样的平凡类型肯定会被填充为零。 - WhozCraig
1
这是不安全的,并且非常依赖编译器。有些情况下是安全的,但这就像玩火一样。 - Benjamin Barrois
7
不,那是错误的。operator[] 会默认构造元素,在此情况下将成员变量初始化为零。“当使用默认分配器时,这将导致从键中复制构造出键,并使映射值进行值初始化。” - Henri Menke
3
@BenjaminBarrois 我并不是说每件事情都会被初始化为零,但在std::unordered_map的操作符[]的情况下,标准保证了值初始化。 - Henri Menke
显示剩余3条评论
1个回答

24
是的,可以安全地假设 Foo 内部的值总是初始化为零,因为 operator[] 的行为会导致使用默认分配器时,键从 key 进行复制/移动构造,并对映射值进行值初始化。

您没有提供构造函数,这意味着 Foo 中的每个字段都将单独进行值初始化,对于原始类型来说,这意味着零初始化。

但是

实际上,您在此面临的问题是,您的映射中不存在名为 "Apple" 的字段。 不幸的是,operator[] 的语义是,如果值不存在,则会即时创建它。 您可能甚至不想访问映射中不存在的字段,并且您正在询问是否总是将其初始化为零,以便可以利用此事实来检查元素是否存在。 但是,为此,您应该使用find()at()成员函数。

  • find()会在元素不存在时返回指向地图末尾的迭代器。 这意味着您可以使用以下方式保护元素访问:

    if (auto apple = map.find("Apple"); apple != map.end()) {
        std::cout << apple->second.num << '\n';
        std::cout << apple->second.state << '\n';
    }
    

    (使用C++17的if语句初始化器)

  • at()方法会在元素未被找到时抛出异常。

  • std::cout << map.at("Apple").num << '\n';
    std::cout << map.at("Apple").state << '\n';
    

    这会导致您的程序崩溃并抛出std::out_of_range异常。您可能会想捕获此异常以检查元素是否存在。请不要这样做。在控制流程中使用异常是非常糟糕的实践。除此之外,抛出异常时速度非常慢。


非常感谢您,先生。 - Zack Lee
@ZackLee 我添加了一些细节。 - Henri Menke

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