C++的std map插入语义的基本原理?

Mar*_* Ba 25 c++ map standard-library

std::map::insert的语义让我有些困惑.我的意思是,我不是在抱怨 - 标准是标准,API就是这样.仍然,

insert

插入操作检查插入的每个元素是否已经在具有相同键值的容器中存在另一个元素,如果是,则不插入该元素,并且不以任何方式更改其映射值.

而且 - 只有在它的单参数版本中pair<iterator,bool> insert ( const value_type& x );它甚至会告诉你它是否甚至将(新的,可能不同的)值插入到键中.据我所知,如果密钥已经存在,迭代器版本将默默地忽略插入.

对我来说,这只是反直觉,我原本期望值部分被覆盖,旧值部分在插入时被丢弃.显然,STL的设计者有不同的想法 - 任何人都知道(历史)的基本原理,或者能够彻底解释现有语义如何(更有意义)?

举例:

在单键映射中实现插入有几种基本方法,例如std::map:

  • 插入,替换,如果已经存在
  • insert,如果已经存在则忽略(这是std :: map的行为)
  • 插入,抛出错误,如果已经存在
  • 插入,UB如果已经存在

我现在试图理解为什么insert_or_ignoreinsert_or_replace(或insert_or_error)更有意义!


我查看了我的TC++ PL副本(不幸的是我只有德语版),有趣的是,Stroustrup在第17.4.1.7章(地图列表操作)中写道:(抱歉粗略翻译德语)

(...)通常情况下,在调用insert()(...)之前,人们不关心是否新插入了密钥(sic!)或已经存在密钥(sic!)

在我看来,它只适用于集合,而不适用于地图,因为对于地图,如果插入了提供的值或旧的遗留在地图中,它确实会产生很大的差异.(这显然与密钥无关,因为密钥是等效的.)


注意:我知道operator[]并且我知道有效STL的第24项和那里提出的efficientAddOrUpdate功能.我只是对insert语义的理由感到好奇,因为我个人觉得它们反直觉.

Kre*_*reg 7

插入方法不是你想要的,它听起来像......插入方法只是做名称暗示...插入值.我同意如果一个值尚未存在而创建一个值的能力,并且在某些情况下替换那里的值是很重要的,但在其他情况下,你真的宁愿不处理异常,返回值等等.只有在值尚未存在时才想进行插入.

这听起来像你正在寻找的方法(如上所述的BoBTFish)可能是[]运营商.只需使用它:

myMap["key"] = "value";
Run Code Online (Sandbox Code Playgroud)

这将通过您的地图找到关键字"key",并用"value"替换相应的值.如果密钥不在那里,它将创建它.这两种方法在不同情况下都非常有用,我发现自己只使用两种方法都需要.

  • 我编辑了你的帖子.首先,不要(明确地)签名,它是皱眉头(无论如何你的用户仍然可见); 第二,请检查降价语法,了解如何格式化内联代码段和多行代码段.谢谢你的回答,欢迎来到SO :) (6认同)

Mat*_* M. 6

我不知道官方的理由,但我会注意到它的二元性operator[].

很明显,人们会喜欢这两种插入方式:

  • 纯添加剂
  • 添加剂/破坏性

如果我们看到一个map数组的稀疏表示,那么存在operator[]有意义.我不知道预先存在的词典是否存在并且是否支持这种语法(也许,为什么不这样).

此外,所有 STL容器都有几个重载insert,接口的这种相似性允许通用编程.

因此,我们至少有两个API的竞争者:operator[]insert.

现在,在C++中,如果您阅读:

array[4] = 5;
Run Code Online (Sandbox Code Playgroud)

自然地,索引处的单元格的内容4已被破坏性地更新.因此,很自然map::operator[]应该返回一个引用以允许这种破坏性更新.

在这一点上,我们现在也需要一个纯粹的加法版本,我们有这种insert方法.为什么不 ?

当然,人们可以给出insert相同的语义operator[],然后继续并在顶部实现一个insert_or_ignore方法.这本来可以做得更多.

因此,虽然我同意它可能会令人惊讶,但我认为我的推理并没有太大的缺陷,可能是我们在这里引导我们的情况的可能解释:)


关于您提出的替代方案:

  • 插入,UB如果已经存在

幸运的是,它不是!

  • 插入,抛出错误,如果已经存在

只有Java(和派生词)才是异常疯狂的.C++是在异常被用于特殊情况的时候构思出来的.

  • 插入,替换,如果已经存在
  • insert,如果已经存在则忽略(这是std :: map的行为)

我们同意选择其中一个.请注意,即使map选出第二个选项,它也不会完全忽略该项目已存在的事实,至少在单个项目版本中,因为它会警告您未插入该项目.