C ++:如何用非零大小初始化地图中的向量

Posted

技术标签:

【中文标题】C ++:如何用非零大小初始化地图中的向量【英文标题】:C++: how to initialize vector in map with non-zero size 【发布时间】:2018-11-17 11:06:05 【问题描述】:

我有一张矢量地图:

std::map<int, std::vector<bool>> mymap

有时,我需要插入一个新元素:

auto& newvec = mymap[42];
// Add stuff to newvec

据我所知(假设地图中还没有 42),这将给我长度为 0 的 newvec(构造为 std::vector&lt;bool&gt; ),然后我可以扩展它。

有没有办法立即将向量初始化为某个大小n

(我不关心性能,只是想知道是否有办法做到这一点)。

【问题讨论】:

您可以将向量 bool 包装在您自己的类中,将其初始化为您想要的大小。 auto &amp; newvec = (mymap[42] = std::vector&lt;bool&gt;(desired_size)); 会这样做,尽管会引入一个可能(理论上)被省略的临时变量。可以说,在定义 newvec 之前执行 mymap[42].resize(desired_size) 或在定义之后立即执行 newvec.resize(desired_size) 更具可读性。 map::emplace 不做你想做的事吗?您可以将其用作 mymap.emplace(42, std::vector(125, false)); 【参考方案1】:

包装std::vector&lt;bool&gt;

您可以按以下方式包装您要初始化的std::vector&lt;bool&gt;

template<size_t N>
struct myvector 
   myvector(): data(N) 
   std::vector<bool> data;
;

然后,将mymap 声明为一个映射,其值类型是这个包装器类型myvector&lt;N&gt;,而不是std::vector&lt;bool&gt;。例如,N 等于 100

std::map<int, myvector<100>> mymap;

如果映射中尚不存在密钥42,则:

auto& newvec = mymap[42];

将创建一个myvector&lt;100&gt; 类型的实例,它依次初始化一个大小为100std::vector&lt;bool&gt;

您可以通过myvectordata 数据成员或执行reinterpret_cast&lt;std::vector&lt;bool&gt;&amp;&gt;(newvec) 来访问创建的std::vector&lt;bool&gt; 对象。


使用std::map::find()std::map::emplace()

另一种方法是使用std::map::find() 而不是std::map::operator[](),通过将返回的迭代器与std::map::end() 返回的迭代器进行比较,首先找出给定的键是否已经存在于映射中。如果给定的键不存在,则使用std::map::emplace() 构造向量。

在您的示例中,newvec 可以通过 三元运算符 为这种方法初始化:

auto it = mymap.find(42); // search for an element with the key 42
bool is_key_in_map = it != mymap.end();
// if the element with the given key exists, then return it, otherwise
// construct it
auto& newvec = is_key_in_map? it->second: 
            mymap.emplace(42, std::vector<bool>(100, true)).first->second;

实际上,您可以直接调用std::map::emplace(),而无需检查给定键是否已存在,但如果该键已存在于映射中,这将花费无用的临时对象(即std::vector&lt;bool&gt; 对象)创建:

auto& newvec = mymap.emplace(42, std::vector<bool>(100, true)).first->second;

C++17 起:std::map::try_emplace()

您可以使用std::map::try_emplace() 代替std::map::emplace()

auto& newvec = mymap.try_emplace(42, 100, true).first->second;

这样,如果映射已经包含给定的键(即,如果它已经包含键 42),则不会构造临时对象 std::vector&lt;bool&gt;(100, true)。因此,这比使用std::map::emplace() 更有效,因为如果没有必要,不会构造临时对象。但是,它确实需要 C++17。

【讨论】:

包装矢量是多余的,我真的不建议这样做。【参考方案2】:

你可以使用map::emplace成员函数:

mymap.emplace(42, std::vector<bool>(125, false));

为键 42 创建一个值 std::vector&lt;bool&gt;(125, false)

正如 ネロク 所提到的,上面的emplace 调用将构造值std::vector&lt;bool&gt;(125, false),即使键42 已经存在于地图中(这也记录在我上面链接的cppreference 页面中)。如果要避免这种情况,您可以先使用map::find 检查该值是否已存在,然后仅在该键不存在时插入该值。那就是:

if (mymap.find(42) == mymap.end()) 
    mymap.emplace(42, std::vector<bool>(125, false));

map::find 和 map::emplace 都具有对数时间复杂度;因此,在 emplace 之前调用 find 在性能关键场景中不会对性能造成太大影响。

【讨论】:

值得一提的是,即使mymap 已经包含该密钥,也会创建临时的std::vector&lt;bool&gt;(125, false)【参考方案3】:

使用map::try_emplace()(或C++17之前的map::emplace()

std::vector 有一个构造函数,它采用初始大小和初始统一值。在您的情况下,假设您希望 125 作为初始大小。对于独立向量,您将使用:

size_t num_bools_we_want = 1234;
std::vector<bool> my_vec(num_bools_we_want, false);

现在,std::map 有一个名为map::try_emplace() 的方法,它将参数转发给值类型的构造函数,这实际上允许您选择它将用于新元素的构造函数。以下是它的使用方法

mymap.try_emplace(42, num_bools_we_want, false);

为键 42 创建一个值 std::vector&lt;bool&gt;(num_bools_we_want, false)。不创建临时向量(无论编译器优化如何)。

这个解决方案的唯一“问题”是try_emplace() 只存在于 C++17 之后。既然您询问了 C++11 - 该标准版本引入了 map::emplace(),除了制作密钥副本的问题外,它的作用几乎相同。有关emplace()try_emplace() 之间区别的讨论,请参阅this question。

【讨论】:

以上是关于C ++:如何用非零大小初始化地图中的向量的主要内容,如果未能解决你的问题,请参考以下文章

如何用C ++制作迷宫中的可移动角色[关闭]

如何用C语言统计一个数据文件中的数据个数

如何用c语言分离字符串中的字母和数字并分别输出

如何用图书馆(地图)和ggplot绘制R中的东非(肯尼亚,乌干达和坦桑尼亚)地图?

如何用c语言分离字符串中的字母和数字并分别输出?

如何用向量表示圆