如果密钥不存在,为什么std :: map operator []会创建一个对象?

n1c*_*ckp 34 c++ stdmap design-rationale

我很确定我已经在某个地方看到了这个问题(comp.lang.c ++?Google似乎也没有在那里找到它)但是这里的快速搜索似乎没有找到它所以这里是:

如果密钥不存在,为什么std :: map operator []会创建一个对象?我不知道,但对我而言,如果你与大多数其他运算符[](如std :: vector)进行比较,这似乎是违反直觉的,如果你使用它,你必须确保索引存在.我想知道在std :: map中实现这种行为的理由是什么.就像我说的那样,当使用无效密钥访问时,更像行动中的索引和崩溃(我猜是未定义的行为)会不会更直观?

看到答案后提炼我的问题:

好到目前为止,我得到了很多答案,说基本上它便宜,所以为什么不是或类似的东西.我完全同意这一点,但为什么不使用专用函数(我认为其中一条评论说在java中没有operator []并且函数被称为put)?我的观点是为什么不映射operator []像vector一样工作?如果我在向量上的超出范围索引上使用operator []我不希望它插入一个元素,即使它很便宜,因为这可能意味着我的代码中的错误.我的观点是为什么地图不一样.我的意思是,对我来说,在地图上使用operator []意味着:我知道这个密钥已经存在(无论出于什么原因,我只是插入它,我在某处有冗余,无论如何).我认为这样会更直观.

那说使用operator []执行当前行为的优点是什么(仅限于此,我同意具有当前行为的函数应该在那里,而不是operator [])?也许它以这种方式提供更清晰的代码?我不知道.

另一个答案是,它已经存在,所以为什么不保留它然后,可能当他们(stl之前的那些)选择实现它,他们发现它提供了一个优势或什么?所以我的问题基本上是:为什么选择以这种方式实现它,这意味着与其他运算符[]有点缺乏一致性.它给了什么好处?

谢谢

R S*_*hko 22

因为operator[]返回对值本身的引用,因此指示问题的唯一方法是抛出异常(通常,STL很少抛出异常).

如果您不喜欢此行为,则可以map::find改为使用.它返回一个迭代器而不是值.这允许它在找不到值时返回一个特殊的迭代器(它返回map::end),但是还要求你取消引用迭代器来获取值.

  • 传统上,地图的界面(在所有语言中,而不仅仅是C++)允许您编写类似`map [key] = value;`的内容,并且地图将替换现有值_或_根据需要插入新的值.这是一个长期存在的惯例,所以"std :: map"跟随它并不奇怪.但是,由于`operator []`无法判断它必须返回的引用是否会在`operator =`的左侧使用,它必须总是假设最坏,因此插入一个新元素. (11认同)
  • 使用[]访问超出范围的向量是未定义的行为.我猜地图也可以在这种情况下调用一些未定义的行为,但你真的喜欢吗?使用向量可以很容易地找出索引是否有效,而不是地图(你可以先调用find,在这种情况下调用[]是没有意义的,因为你应该已经找到了你的项目正在找). (3认同)
  • @ N1ck:不过,它们可能比标准库要老。`:)` (2认同)

Kir*_*sky 13

标准说(23.3.1.2/1)运算符[]返回(*((insert(make_pair(x, T()))).first)).second.这就是原因.它返回参考T&.无法返回无效引用.它返回参考因为我觉得很方便,不是吗?

  • 好的,`operator []`返回引用很方便.所以它应该插入一些东西,因为没有办法返回无效的引用. (2认同)
  • 因为例外是针对特殊情况. (2认同)
  • 几乎沿着尼克斯的想法。每当我使用地图进行查找时,我都不得不最终检查 find 是否返回 end,然后抛出异常,因为这是唯一合适的做法。 (2认同)

AnT*_*AnT 7

回答你真正的问题:没有令人信服的解释为什么这样做."只是因为".

由于std::map是一个关联容器,因此地图中必须存在(或不存在)的键没有明确的预定义范围(与完全不同的情况相反std::vector).这意味着std::map,您需要非插入和插入查找功能.可以[]以非插入方式过载并提供插入功能.或者可以采用相反的方式:[]作为插入运算符重载并为非插入搜索提供函数.所以,某人有时决定采用后一种方法.这就是它的全部内容.

如果他们反过来这样做,也许今天有人会在这里问你的问题的反向版本.