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<bool>
),然后我可以扩展它。
有没有办法立即将向量初始化为某个大小n
?
(我不关心性能,只是想知道是否有办法做到这一点)。
【问题讨论】:
您可以将向量 bool 包装在您自己的类中,将其初始化为您想要的大小。auto & newvec = (mymap[42] = std::vector<bool>(desired_size));
会这样做,尽管会引入一个可能(理论上)被省略的临时变量。可以说,在定义 newvec
之前执行 mymap[42].resize(desired_size)
或在定义之后立即执行 newvec.resize(desired_size)
更具可读性。
map::emplace 不做你想做的事吗?您可以将其用作 mymap.emplace(42, std::vector包装std::vector<bool>
您可以按以下方式包装您要初始化的std::vector<bool>
:
template<size_t N>
struct myvector
myvector(): data(N)
std::vector<bool> data;
;
然后,将mymap
声明为一个映射,其值类型是这个包装器类型myvector<N>
,而不是std::vector<bool>
。例如,N
等于 100
:
std::map<int, myvector<100>> mymap;
如果映射中尚不存在密钥42
,则:
auto& newvec = mymap[42];
将创建一个myvector<100>
类型的实例,它依次初始化一个大小为100
的std::vector<bool>
。
您可以通过myvector
的data
数据成员或执行reinterpret_cast<std::vector<bool>&>(newvec)
来访问创建的std::vector<bool>
对象。
使用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<bool>
对象)创建:
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<bool>(100, true)
。因此,这比使用std::map::emplace()
更有效,因为如果没有必要,不会构造临时对象。但是,它确实需要 C++17。
【讨论】:
包装矢量是多余的,我真的不建议这样做。【参考方案2】:你可以使用map::emplace成员函数:
mymap.emplace(42, std::vector<bool>(125, false));
为键 42
创建一个值 std::vector<bool>(125, false)
。
正如 ネロク 所提到的,上面的emplace
调用将构造值std::vector<bool>(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<bool>(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<bool>(num_bools_we_want, false)
。不创建临时向量(无论编译器优化如何)。
这个解决方案的唯一“问题”是try_emplace()
只存在于 C++17 之后。既然您询问了 C++11 - 该标准版本引入了 map::emplace()
,除了制作密钥副本的问题外,它的作用几乎相同。有关emplace()
和try_emplace()
之间区别的讨论,请参阅this question。
【讨论】:
以上是关于C ++:如何用非零大小初始化地图中的向量的主要内容,如果未能解决你的问题,请参考以下文章