将空的vector插入到std::map()中

3

我如何将一个空的向量嵌入到 std::map 中?例如,如果我有一个 std::map<int, std::vector<int>>,并且我想要 map[4] 包含一个空的 std::vector<int>,我该调用什么函数?


3
map[4] 首先存在吗? - NathanOliver
看起来像是 XY 问题,为什么需要一个空向量? - Slava
5个回答

4
如果您使用operator[](const Key&),当您访问一个不存在的元素时,地图将自动插入一个值初始化的(即在std::vector的情况下,默认构造的)值。请参见此处:http://en.cppreference.com/w/cpp/container/map/operator_at (自C++ 11以来,细节有点更复杂,但在您的情况下,这是要紧的)。
这意味着,如果您的地图为空,而您执行map[4],它将立即给您一个对空(默认构造)向量的引用。分配一个空的向量是不必要的,尽管它可能会使您的意图更加明确。
演示:https://godbolt.org/g/rnfW7g

3

不幸的是,严格正确的答案确实是使用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] = {};

我可能错了,但我认为 OP 在帖子中使用“emplace”时并没有意识到自己在做什么。后面的句子揭示了他们实际想要解决的问题。话虽如此,如果 OP 真的意味着我们应该只使用“emplace”,那么我同意这个答案是“严格正确”的。 - AndyG
@AndyG 对于C++的简单问题,我发现情况就是这样。问题越简单,答案就越复杂和微妙。 - Richard Hodges
看起来他们真的是想使用emplace! - AndyG

2
为了确保空向量位于第4个位置,您可以尝试在第4个位置清空向量,只需使用clear函数即可。
std::map<int, std::vector<int>> my_map;
my_map[4].clear();

正如其他人所提到的,对于std::map的索引运算符,如果指定的索引位置不存在,则会构造一个空值。如果是这种情况,则调用clear是多余的。但是,如果已经存在一个std::vector<int>,则调用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

演示

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

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

3
有趣的是,只需写“my_map[4];”即可。 - Jodocus
@Jodocus:是的,这是由于索引运算符的工作方式导致的。虽然并不总是那么明显。我的几个团队成员也曾被卡住过。编辑:不错的观点,我会加上去的。 - AndyG
@Jodocus:我认为这样更安全,因为可能不想检查是否有任何内容;如果你想要一个空向量,那么最好还是进行分配,因为可能已经存在非空向量。 - AndyG
1
我更喜欢使用 my_map[4].clear(); 而不是赋值。 - Jarod42
@Jarod42:嗯,是的,那肯定更好。 - AndyG

0

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

在现代C++中,这应该可以做到你想要的,而且开销很小或者没有开销。

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

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

最简单的解决方案是 map[4];,不需要赋值。 - Slava

-1

使用 std::make_tuple 和 piecewise_construct:

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对象的第一个参数,以选择构造函数的形式,通过转发两个元组对象的元素到各自的构造函数,就可以就地构造其成员。


2
在我看来,这过于复杂了,而且 OP 特别要求一个空向量。 - AndyG
我会再解释一下。 - Abhishek Keshri
不是downvoter,但是如果你将第二个std::make_tuple改为std :: make_tuple(),那么你的代码实际上会解决OP所问的问题。 - AndyG
谢谢您的建议! - Abhishek Keshri

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