std::地图和它的兄弟姐妹(std::多映射,std::unordered_map/映射)曾经是我最喜欢的容器,当我做竞争编程。事实上,我仍然喜欢它们(尽管现在使用频率较低)。随着现代C++,我们现在有更多的理由使用std:地图。因此,我决定写一篇总结这些新功能的文章来讨论这个话题。所以,没有太多的胡言乱语,让我们直接潜入。

std::地图::包含(C++20)

std::map::contains成员函数是走向代码表达性的好一步。我也厌倦了写作:

Java

 

 
如果(自动搜索 = freq_of freq_of查找2);搜索! = freq_of结束()) |

2
cout<<"Found"<<endl;
3
}
4
假设为 freq_of = 映射<uint32_t,uint32_t>{3, 1},{1, 1},{2},1];

相反,从 C++20 开始,您可以编写:

Java

 

x
1
1
如果(freq_of包含2)|
2
cout<<"Found"<<endl;
3
}

我们编写的代码是先为人类消费而编写的,其次为计算机所编写的。


– 约翰·桑梅斯

std::地图::try_emplace (C++17)

在插入地图时,我们有两种不同的可能性:

  1. 密钥尚不存在。创建新的键值对。
  2. 密钥确实存在。拿现有项目并修改它。

插入元素的典型方法
std::map是通过使用
operator[ ],
std::map::insert
std::map::emplace .但是,在所有这些情况下,我们必须承担默认/专用构造函数或赋值调用的成本。最糟糕的是,如果一个项目已经存在,我们必须删除新创建的项。

Java

 

X
1
14
 
1
int() |
2
量 v=345873524+;
3
地图<uint32_tuint32_tuint32_t>freq_of;
5
用于const自动&nv) |
6
如果(康斯特自动&=插入=freq_offreq_of位置(n1插入){
7
->二 =; // 已存在
8
  }
9
  }
10

11
12

13
返回EXIT_SUCCESS;
14
}

相反

Java

 

X
1

1
如果(康斯特自动&=插入=freq_offreq_oftry_emplace(n1插入){
2
->二 =;
3
}

但是,C++17,有这个
std::try_emplace:::仅在键尚不存在时创建项的方法。这会提高性能,以防创建此类对象的成本很高。

尽管上述示例没有展示创建项目的昂贵性。但是,是的!每当你遇到这样的情况,必须知道如何处理它
std::map::try_emplace.

std::地图::insert_or_assign (C++17)

无论如何,当您必须插入元素时。为了方便起见,您使用
std::地图::运算符=。这是确定(和
危险)!除非对插入或赋值有任何限制

e. 已分配),您必须删除所有小于当前元素的元素。

在这种情况下,
std::map::operator[ ]是不可行的。而
std::map::insert_or_assign 比 更合适,返回的信息比 std::map::operator[ ] 更多。它也不要求映射类型的默认构造性。请考虑以下示例。

Java

 

x
1
19
 
[int ()]

2
量 v=83958+;
3
地图<uint32_tuint32_tuint32_t>freq_of;
4

5
用于自动 &nv) |
6
康斯特自动&is_insertedis_inserted=freq_ofinsert_or_assignn1);
7

if (!is_inserted)\ // 删除所有较小的元素, 然后当前一个, 如果重复

9
freq_of.擦除开始freq_of);
10
  }
11
  }
12
    
13
断言((freq_of=decltypefreq_of
14
[81],
15

16
}));
17

18
返回EXIT_SUCCESS;
19
}

std::地图::插入带提示 (C++11/17)


std::map采取
O(log(n))时间。对于插入新项目,这是相同的。因为插入它们的位置必须向上。天真地插入
M因此,新项目将采取
O(M * log(n))时间。

为了提高效率,
std::map插入函数接受可选的插入提示参数。插入提示基本上是一个活动器,它指向要插入的项目的未来位置附近。如果提示是正确的,那么我们得到摊销
O(1)插入时间。
从性能的角度来看,当项的插入顺序有些可预测时,这非常有用。例如:

Java

 

X
1
20
 
1
()
2
地图<uint32_t->水果-5"葡萄"-2"托摩托"-
3
地图uint32_tuint32_tstring字符串 --2"米克尔"-10"shree"-
地图<uint32_t ,字符串> fruits_and_persons;

5

6
fruits_and_persons.合并水果);
7
断言水果.大小= 00
8

9
fruits_and_persons.合并);
10
断言大小= 11
11
2= "米克尔";// 不会覆盖值 2,即"米克尔"

12

13
断言((fruits_and_persons[decltype水果) ]==
14
  [2"托本"],
15
  [5个 "葡萄"],
16
  [10"shree"],
17
  }));

19
返回EXIT_SUCCESS;
20
}

这里需要注意的是, 当有重复时会发生什么!
不会传输重复的元素。它们被留在右边的地图中

std::地图::提取(C++17)


std::map::merge批量传输元素,
std::map::extract 以及 std::map::insert 转移元素分片。但是,什么是更令人信服的应用
std::map::extract正在修改键。

正如我们知道的,
std::map键始终是唯一和排序的。因此,用户不能修改已插入的地图节点的键至关重要。为了防止用户修改完美排序的地图节点的关键项,
const限定符将添加到键类型。
这种限制是完全有效的,因为它使用户更难使用
std::map错误的方式。但是,如果我们真的需要更改某些地图项的键呢?
在C++17 之前,我们必须删除并重新插入项目以更改密钥。这种方法的缺点是内存分配和释放,这在性能方面听起来很糟糕。但是,从 C++17,我们可以删除和重新插入 std::映射节点,而无需任何内存重新分配。

Java