【问题标题】:Emplace empty vector into std::map()将空向量放入 std::map()
【发布时间】:2018-04-10 13:10:14
【问题描述】:

如何将空向量放入std::map?例如,如果我有一个std::map<int, std::vector<int>>,并且我希望map[4] 包含一个空的std::vector<int>,我可以调用什么?

【问题讨论】:

  • map[4] 是否存在?
  • 看起来像 XY 问题,为什么需要一个空向量?

标签: c++ stl


【解决方案1】:

如果您使用operator[](const Key&),如果您使用了访问不存在的元素。见这里:

http://en.cppreference.com/w/cpp/container/map/operator_at

(从 C++ 11 开始,细节有点复杂,但在你的情况下,这才是最重要的)。

这意味着如果您的地图为空并且您执行map[4],它会很容易地为您提供对空(默认构造)向量的引用。分配一个空向量是不必要的,尽管它可能会让你的意图更清楚。

演示:https://godbolt.org/g/rnfW7g

【讨论】:

    【解决方案2】:

    不幸的是,严格正确的答案确实是使用std::piecewise_construct 作为第一个参数,然后是两个元组。第一个表示创建键 (4) 的参数,第二个表示创建向量的参数(空参数集)。

    看起来像这样:

    map.emplace(std::piecewise_construct,   // signal piecewise construction
                std::make_tuple(4),         // key constructed from int(4)
                std::make_tuple());         // value is default constructed
    

    当然这看起来难看,其他替代方案也可以。他们甚至可能在优化的构建中不再生成代码:

    这个概念上调用了默认构造和移动赋值,但优化器很可能会看穿它。

    map.emplace(4, std::vector<int>());
    

    这调用了默认构造,然后是复制分配。但同样,优化器很可能会看穿它。

    map[4] = {};
    

    【讨论】:

    • 我可能错了,但我认为当他们在帖子中说emplace 时,OP 并没有意识到他们在做什么。后面的句子揭示了他们实际上试图解决的问题。也就是说,如果 OP 真的意味着我们应该只使用 emplace,那么我同意这个答案是“严格正确的”。
    • @AndyG 关于 c++ 的简单问题就像我发现的那样。问题越简单,答案就越复杂和微妙。
    • 看来他们真的是故意使用emplace!
    【解决方案3】:

    为确保将空向量放置在位置 4,您可以简单地尝试 clear 位置 4 的向量。

    std::map<int, std::vector<int>> my_map;
    my_map[4].clear();
    

    正如其他人所提到的,std::map 的索引运算符将在指定索引处构造一个空值(如果不存在)。如果是这种情况,调用clear 是多余的。但是,如果 std::vector&lt;int&gt; 确实存在,则对 clear 的调用用于清除那里的向量,从而产生一个空向量。

    这可能比我之前分配给{} 的方法更有效(见下文),因为我们可能计划在位置 4 处向向量添加元素,并且我们不支付任何新分配的成本这种方式.此外,如果以前使用 my_map[4] 表示将来使用,那么我们的新向量最终可能会调整到与以前几乎相同的大小,这意味着我们可以节省重新分配成本。


    以前的做法:

    只需分配给{},容器就应该在那里正确地构造一个空向量:

    std::map<int, std::vector<int>> my_map;
    my_map[4] = {};
    std::cout << my_map.size() << std::endl; // prints 1
    

    Demo

    编辑:正如 Jodocus 所提到的,如果您知道 std::map 在位置 4 处尚未包含 vector,那么只需尝试访问该位置的向量将默认构造一个,例如:

    std::map<int, std::vector<int>> my_map;
    my_map[4]; // default-constructs a vector there
    

    【讨论】:

    • 有趣的是,写my_map[4];就足够了。
    • @Jodocus:是的,因为索引运算符的工作方式。但它并不总是那么明显。我已经让我的几个团队成员发现了它。编辑:好点,我会补充的。
    • @Jodocus:另外我认为这更安全,因为可能不想检查是否有任何东西可以开始;如果你想要一个空向量,那么最好还是分配,因为那里可能已经有一个非空向量。
    • 我更喜欢my_map[4].clear(); 而不是分配。
    • @Jarod42:嗯,是的,那肯定更好。
    【解决方案4】:

    最简单的解决方案有什么问题? std::map[4] = {};.

    在现代 C++ 中,这应该可以满足您的需求,而无需或至少非常少的开销。

    如果你必须使用emplace,我能想到的最佳解决方案是:

    std::map<int, std::vector<int>> map;
    map.emplace(4, std::vector<int>());
    

    【讨论】:

    • 简单的可能解决方案是 map[4]; 没有分配
    【解决方案5】:

    将分段构造与 std::make_tuple 一起使用:

    map.emplace(std::piecewise_construct, std::make_tuple(4), std::make_tuple());
    

    我们在位置 4 插入一个空向量。

    如果有一般情况,例如,放置一个大小为 100 的向量,然后填充 10:

    map.emplace(std::piecewise_construct, std::make_tuple(4), std::make_tuple(100, 10));
    

    piecewise_construct:这个常量值作为第一个参数传递来构造一个pair对象,通过将两个元组对象的元素转发给它们各自的构造函数来选择构造其成员的构造函数形式。

    【讨论】:

    • 恕我直言,这过于复杂,OP 专门要求提供一个空向量。
    • 我再解释一下。
    • 不是downvoter,但如果您将第二个std::make_tuple 更改为std::make_tuple(),那么您的代码实际上会解决OP 提出的问题。
    • 感谢您的建议!!
    猜你喜欢
    • 2020-03-10
    • 2012-09-28
    • 2019-10-31
    • 1970-01-01
    • 1970-01-01
    • 2017-07-15
    • 2019-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多