正在初始化 std::unordered_map 的 std::list 个元素

Initializing std::list elements of a std::unordered_map

我有一个 unordered_map 类型 <const char *, std::list<void *>>,我有一个很大的值列表,我正在遍历以查看是否在某些条件下匹配。如果匹配,我想将指向某个值的指针附加到值索引处的 std::list。

const char *handler_name = NULL;
while (handler_name = all_handlers->get(), handler_name)
{
    if (condition)
    {
        //Add pointer element to every event handler it registers
        std::list<void *> scripts = responders[handler_name];
        if (scripts.size() == 0)
        {
            responders[handler_name] = scripts;
        }
        scripts.push_back(static_cast<void *> (L));
        active_states[static_cast<void *> (L)] = 1; //set to active so we only delete it once
    }
    handler_name = NULL;
}

这些是我的变量声明:

private:
    std::unordered_map<const char*, std::list<void *>> responders;
    std::unordered_map<void *, int> active_states;

all_handlers 是我正在正确迭代的自定义列表类型,如果非常规地获取所有 const char * C 字符串用作潜在键,如果条件匹配。

然而,gdb 显示 std::list 从未被初始化为感兴趣的键,我认为这与我害怕为 std::list 的每个值初始化 std::list 有关=30=],同时需要在添加新值之前检查条目以查看列表是否已经存在。

在这种情况下进行正确初始化的最佳方法是什么?

假设您的 condition 不是始终为假的表达式,...

您的 list 在条目添加到 responders 地图后立即初始化。那是 C++:你不能有 'uninitialized' 列表。它们是完全构建的,或者它们不存在。 (阅读 RAII

这段代码片段:

    std::list<void *> scripts = responders[handler_name];
    if (scripts.size() == 0)
    {
        responders[handler_name] = scripts;
    }

是空操作。 scripts 变量是 responders[handler_name] 复制的 (值语义!)。如果后者是空的,你只是把它复制回来...

您的意思可能是引用条目中的列表,然后添加:

auto & scripts = responders[handler_name];
scripts.push_back(static_cast<void *> (L));

(注意:如果每个你想知道一个列表是否为空,使用list.empty()...更清楚!Express intent!)

const char* handler_name = NULL;
while (handler_name = all_handlers->get(), handler_name)

这不是编写循环的惯用方法。事实上非常很奇怪。

考虑一种更传统的方法:

const char* handler_name = NULL;
while ((handler_name = all_handlers->get()))

(双括号是必要的,以防止某些编译器发出有关 === 之间可能混淆的警告)。

或使用 for 代替:

for (const char* handler_name = all_handlers->get(); handler_name; handler_name = all_handlers->get())

my fear of initializing a std::list for every value of handler_name,

我不知道这是什么意思。如果映射中还没有,表达式 responders[handler_name] 将为该值初始化一个空的 std::list。但这就是你需要发生的事情。

这是胡说八道:

    std::list<void *> scripts = responders[handler_name];
    if (scripts.size() == 0)
    {
        responders[handler_name] = scripts;
    }
    scripts.push_back(static_cast<void *> (L));

首先它计算 responders[handler_name],其中 returns 对应于该键的 std::list(如果不存在则创建一个空键)。

然后将该列表复制到一个名为 scripts 的新对象中。

然后如果 scripts 不为空,它 通过复制 scripts 替换 映射中的值( 必须 完全没有意义,因为 scripts 是映射中值的副本,所以如果 scripts 不为空,则映射中的值已经不为空)。

最后你修改列表的copyscripts,这是一个在循环结束时超出范围的局部变量,所以你永远不要向存储在地图中的列表添加任何条目。

and simultaneously needing to check the entry to see if the list already exists before appending a new value.

你不需要那样做。 responses[handler_name] 为您完成。如果没有该密钥的条目,将创建一个。

What is the best way to do proper initialization in this case?

不要再无所事事,然后编写无意义的代码来尽量避免非问题。 responses[handler_name] 执行您需要的所有初始化。它查找与该键对应的列表,如果未找到列表,则正确初始化一个空列表。

一大堆废话可以简单地写成:

    responses[handler_name].push_back(static_cast<void *> (L));
    active_states[static_cast<void *> (L)] = 1; //set to active so we only delete it once

static_cast<void*> 可能甚至不是必需的,因为任何指针都会隐式转换为 void*。如果您想更明确地了解转换,可以保留它们,但我会简单地写:

for (auto handler_name = all_handlers->get(); handler_name; handler_name = all_handlers->get())
{
    if (condition)
    {
        //Add Lua state to every event handler it registers
        responses[handler_name].push_back(L);
        active_states[L] = 1; //set to active so we only delete it once
    }
}

这更短、更简单,并且不包含用于解决不存在的问题的混乱代码。