关联容器

掌握map/set/unordered_map/unordered_set等八种关联容器——理解有序比较与无序哈希契约,知道何时选有序、何时选无序,能用map做键值映射、用set做去重与存在判断

学习目标

  • 能实现用 map 完成键值映射——通过 key 查找 value、安全地区分 find() 与会插入默认值的 operator[],并用 C++11 迭代器访问 first/second
  • 能比较并选择合适的关联容器——需要按键排序或范围查询选 map/set,哈希与相等判断便宜且无需顺序时评估无序容器,需要同一 key 多个值选 multimap
  • 能回答:一个程序用 map<string,int> 统计单词出现次数,写出 ++word_count[word] 后,word_count["apple"] 返回什么?用 mapunordered_map 分别实现有什么区别?

机制总览

关联容器:机制路径

  1. 1

    直觉:为什么位置编号不够用?

    你学过了顺序容器——用 vector 按位置 v[3] 访问元素。但想想这些场景:查电话号码——你输入名字 "Alice",想知道她的号码——你不可能说「我要第 5 个元素」——你不知道 Alice 在第几个位置。你需要的是按「名字」直接找到「号码」——这就是键值对的核心:通过 key 找到 value。

  2. 2

    种容器,两大阵营

    在 C++ 里, 关联容器(associative container) 把你的元素按 key 组织——key 就是你的搜索字——通过它定位数据。

  3. 3

    官方 Chapter 11 的键契约与操作边界

    原书的核心不是记住八个名字,而是理解有序容器如何判断键等价、无序容器如何配对哈希与相等关系,以及 insert 、 find 、下标和删除分别会不会修改容器。

先按顺序建立机制,再进入实验切换阶段并检查失效证据。

章级决策实验

关联容器:机制与证据

切换《关联容器》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。

选择推理阶段

当前阶段 · 直觉:为什么位置编号不够用?

你学过了顺序容器——用 vector 按位置 v[3] 访问元素。但想想这些场景:查电话号码——你输入名字 "Alice",想知道她的号码——你不可能说「我要第 5 个元素」——你不知道 Alice 在第几个位置。你需要的是按「名字」直接找到「号码」——这就是键值对的核心:通过 key 找到 value。

可核验证据

保留编译诊断,运行本节最小示例,并用边界断言、对象计数或 sanitizer 复核「直觉:为什么位置编号不够用?」的契约。

学完《关联容器》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。

失效—证据矩阵

关联容器:失效与核验

直觉:为什么位置编号不够用?

典型失效

若把「直觉:为什么位置编号不够用?」当作孤立语法点,忽略类型约束、对象生命周期或库契约,代码即使通过编译也可能破坏不变量。

核验证据

保留编译诊断,运行本节最小示例,并用边界断言、对象计数或 sanitizer 复核「直觉:为什么位置编号不够用?」的契约。

种容器,两大阵营

典型失效

若把「种容器,两大阵营」当作孤立语法点,忽略类型约束、对象生命周期或库契约,代码即使通过编译也可能破坏不变量。

核验证据

保留编译诊断,运行本节最小示例,并用边界断言、对象计数或 sanitizer 复核「种容器,两大阵营」的契约。

官方 Chapter 11 的键契约与操作边界

典型失效

若把「官方 Chapter 11 的键契约与操作边界」当作孤立语法点,忽略类型约束、对象生命周期或库契约,代码即使通过编译也可能破坏不变量。

核验证据

保留编译诊断,运行本节最小示例,并用边界断言、对象计数或 sanitizer 复核「官方 Chapter 11 的键契约与操作边界」的契约。

每个判断都必须能落到观测、测试或产物,不能只凭代码表面推测。

直觉:为什么位置编号不够用?

你学过了顺序容器——用 vector 按位置 v[3] 访问元素。但想想这些场景:查电话号码——你输入名字 "Alice",想知道她的号码——你不可能说「我要第 5 个元素」——你不知道 Alice 在第几个位置。你需要的是按「名字」直接找到「号码」——这就是键值对的核心:通过 key 找到 value。

想象工厂里有一本分类索引目录(map)——条目始终按规则排序,既能单点查找,也能从某个边界开始连续浏览。还有一种快速检索终端(unordered_map)——它先用哈希把类别分到桶里,平均查找为常数时间,但不提供排序与范围语义。选择取决于操作分布、键成本和顺序需求,不是简单的“一个慢、一个快”。

没有关联容器的世界会怎样? 你只能用两个 vector 并行维护——一个存名字、一个存号码——每次查名字都得遍历整个数组(O(n))——一万条数据就要走一万步。删除 Alice 的条目后两个 vector 要同步移动——复杂、易出错、慢。这一章教你「以 key 为核心」的数据组织方式——查找、插入、删除都是 O(log n) 甚至 O(1)。

八种容器,两大阵营

在 C++ 里, 把你的元素按 key 组织——key 就是你的搜索字——通过它定位数据。

关联容器:有序(红黑树)vs 无序(哈希表)有序关联容器(红黑树)key₃key₁<key₅>key₀<key₂>key₄<key₆>红节点黑节点map / setmap: key→value键值对set: 只存key(去重判存在)按key有序排列、不可重复keymultimap / multisetmultimap: 允许重复keymultiset: 允许重复key仍按key有序,但允许多个同key时间复杂度插入 insertO(log n)删除 eraseO(log n)查找 findO(log n)遍历(中序)O(n) 按键序内存结构每个节点含left/right/parent+color指针无序关联容器(哈希表)桶数组 (buckets)桶0桶1桶2桶3桶4桶5桶6桶7key₂key₁₀key₁₈hash(key) % bucket_count → 桶索引同桶内拉链法(chaining)解决冲突unordered_map / unordered_setunordered_map: key→valueunordered_set: 只存key无序、不可重复key、均摊O(1)unordered_multimap / setunordered_multimap: 重复keyunordered_multiset: 重复key无序、允许重复key、均摊O(1)时间复杂度插入 insert均摊 O(1)删除 erase均摊 O(1)查找 find均摊 O(1)遍历O(n) 无序内存桶数组 + 链表/节点有序容器底层为红黑树(自平衡二叉搜索树)→ O(log n);无序容器底层为哈希表(桶+链)→ 均摊 O(1),但最坏 O(n)
八大关联容器分为两大阵营:有序容器(set/map/multiset/multimap)底层用红黑树,元素按键排序;无序容器(unordered_set/map/multiset/multimap)底层用哈希表,均摊 O(1) 但元素无序。

上图展示了两大阵营的底层结构差异:

  • 有序容器(map/set/multimap/multiset) 通常以 实现——元素按比较器定义的顺序遍历,查找、插入、删除具有对数复杂度。这里的树图是常见实现模型,不是标准强制的对象布局。
  • 无序容器(unordered_map/set/multimap/multiset) 使用 语义——查找、插入平均 O(1),最坏 O(n)。遍历顺序未指定,插入或 rehash 后可能改变,不能作为业务契约。

重要的区别——有序容器名只有核心词(map/set),无序容器名字前面加 unordered_ 前缀。

官方 Chapter 11 的键契约与操作边界

原书的核心不是记住八个名字,而是理解有序容器如何判断键等价、无序容器如何配对哈希与相等关系,以及 insertfind、下标和删除分别会不会修改容器。

有序键:比较器定义等价关系

有序关联容器默认使用 std::less<Key>。两个键是否“相同”并不直接由 operator== 决定,而是看比较器:若 comp(a,b)comp(b,a) 都为 false,它们在容器中等价。比较器必须形成严格弱序;把 <= 用作比较会破坏容器不变量。

#include <algorithm>
#include <cctype>
 
struct CaseInsensitiveLess {
    bool operator()(const std::string &a, const std::string &b) const {
        return std::lexicographical_compare(
            a.begin(), a.end(), b.begin(), b.end(),
            [](char x, char y) {
                return std::tolower(static_cast<unsigned char>(x)) <
                       std::tolower(static_cast<unsigned char>(y));
            });
    }
};
 
std::map<std::string, int, CaseInsensitiveLess> counts;

因此 key 在 mapvalue_type 中是 const,set 的元素也不能原地修改:若键变化却不重新定位,顺序将被破坏。需要改键时应删除旧元素并插入新元素。

无序键:Hash 与 KeyEqual 必须一致

无序容器同时保存哈希函数与键相等函数。若 key_equal(a,b) 为 true,则 hasher(a)hasher(b) 必须相同;反过来哈希值相同不代表键相等,容器仍会比较键来处理冲突。自定义大小写不敏感键时,哈希与相等必须采用同一规范化规则。

bucket_countload_factormax_load_factorrehash 暴露桶接口,但标准不规定桶内一定是链表,也不规定桶号必须用简单取模计算。rehash 会使迭代器失效;元素引用和指针仍保持有效,删除则只使被删元素的引用、指针和迭代器失效。

插入、下标与范围查询

唯一键容器的 insert 返回 pair<iterator,bool>,multi 容器只返回指向新元素的迭代器。map::operator[] 在键缺失时插入值初始化的 mapped value,因此只适合“查找或创建”;纯查询用 findlower_boundupper_boundequal_range 依赖有序比较,能表达区间与重复键范围;无序容器不提供排序范围查询。

map——你的键值字典

是最常用的关联容器——你给它 key,它返回关联的 value——像一个字典。

#include <map>
#include <string>
#include <iostream>
 
int main() {
    std::map<std::string, int> phonebook;
 
    // 三种插入方式
    phonebook["Alice"] = 5551234;            // operator[] 插入
    phonebook.insert({"Bob", 5555678});      // insert + 列表初始化
    phonebook.emplace("Charlie", 5559012);   // emplace 原地构造
 
    // 查找——用 find,别用 []([] 在 key 不存在时会插入默认值)
    auto it = phonebook.find("Alice");
    if (it != phonebook.end())
        std::cout << it->second << '\n';     // 5551234
 
    // C++11 遍历——元素类型是 pair<const string, int>
    for (std::map<std::string, int>::const_iterator it = phonebook.begin();
         it != phonebook.end(); ++it)
        std::cout << it->first << ": " << it->second << '\n';
    // 输出:Alice: 5551234  Bob: 5555678  Charlie: 5559012
    // —— 按键的字母序排列
}

注意遍历结果按比较器定义的 key 顺序排列(默认字典序)。标准保证顺序与对数复杂度,但不要求实现必须暴露红黑树或中序遍历细节。

set——只存 key 的去重集合

是 map 的「value-less」版——你只关心某个 key 是否存在或维护一个不重复的集合。

#include <set>
#include <string>
#include <iostream>
 
int main() {
    std::set<std::string> whitelist = {"admin", "root", "deploy"};
 
    // C++11 检查是否存在
    if (whitelist.find("admin") != whitelist.end())
        std::cout << "通过\n";
 
    // 插入 + 检查是否成功
    std::pair<std::set<std::string>::iterator, bool> result =
        whitelist.insert("guest");
    std::cout << (result.second ? "插入成功" : "已存在") << '\n';
 
    // 遍历——有序(字母序)
    for (const auto &u : whitelist)
        std::cout << u << ' ';  // admin deploy guest root
}

insert 返回 pair<iterator, bool>——second=true 表示插入成功(是新 key),false 表示 key 已存在。

当 key 需要重复——multimap 和 multiset

有时候一个 key 需要对应多个值——比如一个作者写了多本书、一个学生选多门课。这时用

#include <map>
#include <iostream>
 
int main() {
    std::multimap<std::string, std::string> books;
    books.insert({"Orwell", "1984"});
    books.insert({"Orwell", "Animal Farm"});
    books.insert({"Huxley", "Brave New World"});
 
    // 查找 Orwell 的所有书
    std::pair<std::multimap<std::string, std::string>::iterator,
              std::multimap<std::string, std::string>::iterator> range =
        books.equal_range("Orwell");
    for (std::multimap<std::string, std::string>::iterator it = range.first;
         it != range.second; ++it)
        std::cout << it->second << ' ';  // 1984 Animal Farm
}

multimap/multiset 没有 operator[]——如果一个 key 有多个值、应该返回哪一个?没有唯一答案。取而代之用 equal_range 拿到所有值。

pair——两个值的简单打包

出现在关联容器的各个角落——insert 的返回值是 pair、map 的每个元素是 pair、equal_range 的返回值也是 pair。

#include <utility>
#include <string>
 
// 创建 pair 的三种方式
std::pair<int, std::string> p1(42, "answer");
auto p2 = std::make_pair(42, "answer");  // 自动推导类型
std::pair<int, std::string> p3 = {42, "answer"};  // C++11 列表初始化
 
// 访问
std::cout << p1.first << ' ' << p1.second;  // 42 answer
 
// map 的 insert 返回值
std::map<int, std::string> m;
auto ret = m.insert({1, "one"});
// ret.first  是迭代器,指向 {1, "one"}
// ret.second 是 bool,值为 true(插入成功)

若项目升级到 C++17,可用结构化绑定简化 pair 解包;这不是本书 C++11 基线语法:

auto [iter, success] = m.insert({2, "two"});   // 解包 pair<iterator, bool>
for (const auto &[key, val] : m)               // 解包 pair<const Key, T>
    std::cout << key << " -> " << val << '\n';

unordered_map——哈希表的速度

关联容器:有序(红黑树)vs 无序(哈希表)有序关联容器(红黑树)key₃key₁<key₅>key₀<key₂>key₄<key₆>红节点黑节点map / setmap: key→value键值对set: 只存key(去重判存在)按key有序排列、不可重复keymultimap / multisetmultimap: 允许重复keymultiset: 允许重复key仍按key有序,但允许多个同key时间复杂度插入 insertO(log n)删除 eraseO(log n)查找 findO(log n)遍历(中序)O(n) 按键序内存结构每个节点含left/right/parent+color指针无序关联容器(哈希表)桶数组 (buckets)桶0桶1桶2桶3桶4桶5桶6桶7key₂key₁₀key₁₈hash(key) % bucket_count → 桶索引同桶内拉链法(chaining)解决冲突unordered_map / unordered_setunordered_map: key→valueunordered_set: 只存key无序、不可重复key、均摊O(1)unordered_multimap / setunordered_multimap: 重复keyunordered_multiset: 重复key无序、允许重复key、均摊O(1)时间复杂度插入 insert均摊 O(1)删除 erase均摊 O(1)查找 find均摊 O(1)遍历O(n) 无序内存桶数组 + 链表/节点有序容器底层为红黑树(自平衡二叉搜索树)→ O(log n);无序容器底层为哈希表(桶+链)→ 均摊 O(1),但最坏 O(n)
八大关联容器分为两大阵营:有序容器(set/map/multiset/multimap)底层用红黑树,元素按键排序;无序容器(unordered_set/map/multiset/multimap)底层用哈希表,均摊 O(1) 但元素无序。

右边展示了无序容器的哈希表结构。unordered_map 用哈希表替代红黑树——插入、查找都是均摊 O(1)。

用 Stepper 一步步看哈希表如何工作:

先预测:两个不同 key 得到相同哈希值时,它们是否必须相等?插入触发 rehash 后,之前保存的迭代器、引用和指针分别还能不能用?先写下判断,再用下面的桶示意与键契约核对。

分步1 / 3

桶数组——哈希表的骨架

哈希表最底层是一个桶数组——每个桶是一个槽位。没有元素时所有桶都是空的。

unordered_map 内部结构:哈希表Bucket Array桶数组 (bucket_count)bucket[0]bucket[1]bucket[2]bucket[3]bucket[4]bucket[5]bucket[6]bucket[7]关键操作与复杂度插入 insert均摊 O(1)查找 find均摊 O(1)删除 erase均摊 O(1)rehashO(n)负载因子 (load_factor)定义size / bucket_count超阈值触发 rehash(重新分配桶+重新散列)默认max_load_factor() ≈ 1.0策略rehash(n) 手动设桶数
哈希表的基础:bucket 数组。每个桶是一个槽位,等待放入 key-value 对。

"桶数"不是固定的——当元素增多、负载因子过高时——自动 rehash——重新分配更多桶。

关键控制——你可以手动调优哈希表:

std::unordered_map<std::string, int> m;
 
m.reserve(10000);       // 按当前 max_load_factor 为约 10000 个元素准备桶
m.rehash(20000);        // 手动设桶数为 20000
m.max_load_factor(0.5); // 降负载因子——更稀疏的桶=更快查找=更多内存
m.load_factor();        // 当前负载因子 = size/bucket_count

选一个容器——你指向哪里?

来做一个决策——你有一个场景,该选八种关联容器中的哪个?走一遍这个决策流。

为你的场景选对关联容器需要 key→value映射吗?YES ↙NO ↗不需要 kv 映射考虑用顺序容器vector/list/deque...需要按键排序吗?YES(有序)NO(无序)unordered_setunordered_mapunordered_multisetunordered_multimapkey 允许重复吗?NOsetmapYESmultisetmultimap无序 + 不可重复key → unordered_set / unordered_map均摊 O(1) 查找/插入,无序遍历——当你不需要有序输出、只关心「快速查找」时选这个无序 + 可重复key → unordered_multiset / unordered_multimap场景极罕见——大多数时候你不需要无序+重复;先确认是否真的是这个需求
完整决策链:有序容器(set/map/multiset/multimap)vs 无序容器(unordered_...),选对的才能事半功倍。

用 Stepper 跟着走——每一步做一个判断:

分步1 / 4

第一问:需要 key→value 映射吗?

第一个问题把关联容器和顺序容器分开——如果你不需要通过 key 找 value——你只是按位置访问、在末尾追加——那回到顺序容器(vector/list/deque)。

为你的场景选对关联容器需要 key→value映射吗?YES ↙NO ↗不需要 kv 映射考虑用顺序容器
第一步:确认是否需要 key→value 映射。不需要?回到顺序容器。

No → 顺序容器 | Yes → 继续

实战:四个经典场景

接下来用四个真实场景练习——对每种场景选对容器并写出实现。

场景一:单词统计器——map 统计频率

#include <map>
#include <string>
#include <iostream>
 
int main() {
    std::map<std::string, int> word_count;
    std::string word;
 
    while (std::cin >> word)
        ++word_count[word];  // 不存在则插入 {word,0}→++变1
 
    for (std::map<std::string, int>::const_iterator it = word_count.begin();
         it != word_count.end(); ++it)
        std::cout << it->first << ": " << it->second << '\n';
}

++word_count[word] 这行包含了隐式的「不存在时插入默认值 0」——是 map 的 operator[] 的经典用法。如果你用 find——代码会长得多——但更显式:auto it = word_count.find(word); if (it == word_count.end()) word_count[word] = 1; else ++it->second;

选 unordered_map 则是均摊 O(1) 插入、输出无序——如果不需要字母序更推荐。

场景二:去重检查器——set 判存在

#include <set>
#include <string>
#include <iostream>
 
int main() {
    std::set<std::string> seen;
    std::string word;
 
    while (std::cin >> word) {
        if (!seen.insert(word).second)   // 已存在——重复
            std::cout << word << " 重复了!\n";
        // seen.insert(word).second == true → 首次出现,已插入
    }
}

seen.insert(word) 返回 pair<iterator, bool>——.second 直接告诉你是不是首次。一行就把「判断+插入」做完了——没有额外查找。

场景三:无序翻译器——unordered_map 的高速查找

#include <unordered_map>
#include <string>
#include <iostream>
 
int main() {
    std::unordered_map<std::string, std::string> dict;
 
    dict.reserve(20000);  // 预分配——避免 rehash
 
    // 建词库阶段
    dict["hello"] = "你好";
    dict["world"] = "世界";
 
    // 查询阶段——均摊 O(1)
    std::string query;
    while (std::cin >> query) {
        auto it = dict.find(query);
        if (it != dict.end())
            std::cout << it->second << '\n';
        else
            std::cout << query << " 未找到\n";
    }
}

find() 而不是 dict[query] 来做只读查找——避免 operator[] 把不存在的 query 也插入。用 reserve(20000) 预分配桶——避免多次 rehash(在加载词库阶段特别重要)。

场景四:区间定价器——map 的范围查询(lower_bound)

#include <map>
#include <iostream>
 
int main() {
    // 分段边界 → 费率:每个阈值是「该级起步价」
    std::map<int, double> tiers = {
        {0, 0.05},    // 0-99  费率 5%
        {100, 0.08},  // 100-199 费率 8%
        {200, 0.12},  // 200+   费率 12%
    };
 
    int price = 150;
    auto it = tiers.upper_bound(price);  // 第一个 >price 的边界
    --it;
    std::cout << "价格 " << price << " 费率: " << it->second << '\n';
    // 输出: 价格 150 费率: 0.08
}

upper_bound(150) 返回指向 {200, 0.12} 的迭代器(第一个 >150 的边界)——往回退一步(--it)就是 {100, 0.08}——150 落在它的区间。这就是有序容器的范围查询能力——unordered_map 做不到。

选型速查

你的场景选这个
需要 key→value 映射、key 唯一、需要按键遍历map
只关心 key 是否存在、需要按键排列set
key→value、key 可重复(一对多)multimap
只关心 key 是否存在、key 可重复、需有序multiset
key→value、key 唯一、平均查找优先且无需顺序unordered_map
只关心存在、哈希与相等判断成本合适unordered_set
key→value、key 可重复、无序unordered_multimap
只关心存在、key 可重复、无序unordered_multiset

容易踩的坑

小结

  • 关联容器通过 key 存取——有序容器按比较器顺序遍历并提供对数复杂度与范围查询;无序容器用 Hash/KeyEqual 组织桶,平均 O(1)、最坏 O(n),遍历顺序未指定
  • map 是 key→value 的字典——m[k] 自动插入默认值(有副作用)、find(k) 纯查找不插入、at(k) 抛异常;遍历顺序 = 按键排序;set 是只存 key 的去重集合
  • pair 是 map 的构成单元——pair<const K, V> 的 key 不可修改;C++11 用 first/second,C++17 才可用结构化绑定简化解包
  • 无序容器通过 hash、bucket 与 key equality 工作——reserve(n) 按负载因子准备桶、max_load_factor(x) 调控密度;冲突存储与桶映射公式由实现决定
  • 选容器三步问——需要 key→value 就选 map 系否则 set 系→要求有序输出选 map/set 否则选 unordered_→允许多个同 key 就加 multi 前缀

练习

问题 1(改代码型) 下面代码想检查 "admin" 是否在 set 中并打印——找出 bug 并写出修正。

#include <set>
#include <string>
#include <iostream>
int main() {
    std::set<std::string> users = {"alice", "bob"};
    std::cout << users["admin"];  // 打印 admin 的状态?
    return 0;
}

问题 2(独立实现题) 写一个程序:用 multimap<string, string> 实现一个「书籍作者索引」。支持插入书名和作者名(一个作者可以写多本书),然后输入一个作者名——输出他的所有书。用 equal_range 实现查询部分。

问题 3(选型题) 以下三个场景分别应该选哪个容器?各用一句话解释原因。

  • A:需要按日期(int key)排序输出财务流水——每个月有几千条记录,需要支持范围查询「查询 2024年1月 到 3月 的所有记录」。
  • B:需要快速检查一个 URL 是否已经被爬虫访问过——URL 数量在千万级——只关心存在性、不需要排序输出。
  • C:需要建立 IP 地址到物理地址的映射——每个 IP 唯一对应一个国家/城市——数据量在亿级——希望查询时能 O(log n) 找到对应的地理信息。

名词解释

名词解释

本章出现的专业名词,用大白话再讲一遍。

关联容器(associative container)

按键组织元素而不是按位置索引的容器。四种有序容器由比较器维护顺序,四种无序容器由哈希与键相等关系组织桶;map 系存键值对,set 系只存键。详见本章契约图。

红黑树(red-black tree)

常见的自平衡二叉搜索树,通过颜色与旋转限制树高。许多标准库用它满足有序关联容器的顺序和对数复杂度要求,但 C++ 标准不指定必须使用红黑树,也不规定节点布局。

哈希表(hash table)

通过 Hash 把键组织到桶,再用 KeyEqual 区分等价键的数据结构。分布良好时操作平均 O(1),最坏可退化到 O(n)。标准暴露桶接口,但不规定桶内必须是链表或桶号必须用取模计算。

map

存储唯一键到值映射的有序关联容器。遍历顺序由比较器决定,value_type 是 pair<const Key, T>m[k] 在键缺失时会插入值初始化对象,纯查询应使用 find。

set

只存唯一键的有序关联容器。元素顺序由比较器决定,适合存在性、去重和范围查询;没有 operator[],insert 返回的 pair 中 bool 表示是否插入了新键。

multimap/multiset

允许同一 key 出现多次的关联容器。multimap 是 map 的去唯一约束版(一个 key→多个 value);multiset 是 set 的去唯一约束版(key 可重复)。仍然按 key 排序,但没有 operator[](一个 key 对应多个 value 没有唯一返回)。查找用 equal_range 或 lower_bound/upper_bound 取得该 key 的所有元素范围。详见本章「当 key 需要重复」一节的代码示例。

pair

一个简单的模板结构(在 <utility> 中),保存两个值:first(第一个)和 second(第二个)。map 的 value_type 就是 pair<const Key, T>。map 的 insert 返回值也是 pair(pair<iterator, bool>)。创建 pair 的三种方式:构造 pair<int,string>(42,"x")、make_pair(42,"x")、列表 {42,"x"}。详见本章「pair——两个值的简单打包」一节的代码示例。

原版目录概念补充核对

以下条目补齐官方目录中容易被示例主线掩盖的概念。它们不重复罗列目录,而是明确每项概念的机制、适用边界和验收证据。

关键字类型要求:机制、边界与证据

在《关联容器》的官方单元 cppp-11 中,关键字类型要求连接本章第 2 组知识约束。学习时要同时说明它接受什么输入、改变什么状态、在何种边界失效;再以本章示例的编译诊断、固定输入输出或失败用例复核结论,不能只记术语名称。

资料与写作方式声明

本章以C++ Primer, Fifth Edition, Chapter 11: Associative Containers权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…