← 返回蜂巢洞察

使用 std:::明智地与现代C++

std::地图和它的兄弟姐妹(std::多映射,std::unordered_m … Read More

Socrates

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

 

{ elem.addEventListener(event, (e) => fn(e, elem), false); }); } } const onLoad = function () { let lastKnownScrollPosition = window.scrollY; let ticking = true; attachEvent('#header nav', 'click', function () { document.querySelector('[data-aw-toggle-menu]')?.classList.remove('expanded'); document.body.classList.remove('overflow-hidden'); document.getElementById('header')?.classList.remove('h-screen'); document.getElementById('header')?.classList.remove('expanded'); document.getElementById('header')?.classList.remove('bg-page'); document.querySelector('#header nav')?.classList.add('hidden'); document.querySelector('#header > div > div:last-child')?.classList.add('hidden'); }); attachEvent('[data-aw-toggle-menu]', 'click', function (_, elem) { elem.classList.toggle('expanded'); document.body.classList.toggle('overflow-hidden'); document.getElementById('header')?.classList.toggle('h-screen'); document.getElementById('header')?.classList.toggle('expanded'); document.getElementById('header')?.classList.toggle('bg-page'); document.querySelector('#header nav')?.classList.toggle('hidden'); document.querySelector('#header > div > div:last-child')?.classList.toggle('hidden'); }); attachEvent('[data-aw-toggle-color-scheme]', 'click', function () { if (defaultTheme.endsWith(':only')) { return; } Observer.removeAnimationDelay(); document.documentElement.classList.toggle('dark'); localStorage.theme = document.documentElement.classList.contains('dark') ? 'dark' : 'light'; }); attachEvent('[data-aw-social-share]', 'click', function (_, elem) { const network = elem.getAttribute('data-aw-social-share'); const url = encodeURIComponent(elem.getAttribute('data-aw-url')); const text = encodeURIComponent(elem.getAttribute('data-aw-text')); let href; switch (network) { case 'facebook': href = `https://www.facebook.com/sharer.php?u=${url}`; break; case 'twitter': href = `https://twitter.com/intent/tweet?url=${url}&text=${text}`; break; case 'linkedin': href = `https://www.linkedin.com/shareArticle?mini=true&url=${url}&title=${text}`; break; case 'whatsapp': href = `https://wa.me/?text=${text}%20${url}`; break; case 'mail': href = `mailto:?subject=%22${text}%22&body=${text}%20${url}`; break; default: return; } const newlink = document.createElement('a'); newlink.target = '_blank'; newlink.href = href; newlink.click(); }); const screenSize = window.matchMedia('(max-width: 767px)'); screenSize.addEventListener('change', function () { document.querySelector('[data-aw-toggle-menu]')?.classList.remove('expanded'); document.body.classList.remove('overflow-hidden'); document.getElementById('header')?.classList.remove('h-screen'); document.getElementById('header')?.classList.remove('expanded'); document.getElementById('header')?.classList.remove('bg-page'); document.querySelector('#header nav')?.classList.add('hidden'); document.querySelector('#header > div > div:last-child')?.classList.add('hidden'); }); function applyHeaderStylesOnScroll() { const header = document.querySelector('#header[data-aw-sticky-header]'); if (!header) return; if (lastKnownScrollPosition > 60 && !header.classList.contains('scroll')) { header.classList.add('scroll'); } else if (lastKnownScrollPosition <= 60 && header.classList.contains('scroll')) { header.classList.remove('scroll'); } ticking = false; } applyHeaderStylesOnScroll(); attachEvent([document], 'scroll', function () { lastKnownScrollPosition = window.scrollY; if (!ticking) { window.requestAnimationFrame(() => { applyHeaderStylesOnScroll(); }); ticking = true; } }); }; const onPageShow = function () { document.documentElement.classList.add('motion-safe:scroll-smooth'); const elem = document.querySelector('[data-aw-toggle-menu]'); if (elem) { elem.classList.remove('expanded'); } document.body.classList.remove('overflow-hidden'); document.getElementById('header')?.classList.remove('h-screen'); document.getElementById('header')?.classList.remove('expanded'); document.querySelector('#header nav')?.classList.add('hidden'); }; window.onload = onLoad; window.onpageshow = onPageShow; document.addEventListener('astro:after-swap', () => { initTheme(); onLoad(); onPageShow(); }); })();