为什么我们不能在map中有一个不可变版本的operator[]?

4
以下代码运行良好:
std::map<int, int>& m = std::map<int, int>();
int i = m[0];

但不包括以下代码:

// error C2678: binary '[' : no operator...
const std::map<int, int>& m = std::map<int, int>();
int i = m[0];

大多数情况下,我更喜欢让我的大部分东西变成不可变的,原因是:

http://www.javapractices.com/topic/TopicAction.do?Id=29

我查看地图源代码。它有:
mapped_type& operator[](const key_type& _Keyval)

有没有任何原因,导致std::map无法提供

const mapped_type& operator[](const key_type& _Keyval) const

2
现在Roger Pate已经纠正了我(现已删除的)错误答案,我发现这个问题更有趣了——为什么不使用const版本的operator[],如果条目不在映射中,则抛出异常(或具有未定义的行为)? - Michael Burr
@Michael:我完全同意。我从来没有认为“好吧,只需使用find和check!”是一个很好的答案;它更加笨拙。在vector中,at会抛出异常,为什么map中的operator[] const不能抛出异常呢? - GManNickG
4个回答

6
原因在于std::map的语义规定,如果您尝试访问不存在的键的元素,则会创建一个默认构造的元素。换句话说,如果位置0上不存在int,则m[0]将在该位置创建一个int。显然,这与const map不兼容。
您可能会说“好吧,制作一个operator[]的const版本,并使其不执行此操作!”但是有两个问题:语义上的差异将是非明显且令人困惑的,并且如果您尝试访问不存在的键,不清楚应该发生什么(抛出异常?)。
相反,您应该使用地图上的find()方法,它将返回指向您要查找的键/值对的迭代器。查找与operator[]完全一样有效,可以用于const maps(在这种情况下返回一个const迭代器),并且如果键不存在,它将返回end()迭代器。

我不确定是否订阅“confusing”参数,因为正如这个用户所演示的那样,它本身就很令人困惑。而且map.find(key)->second并不像map[key]那样好用。考虑到已经存在所有的“未定义行为”,再增加一个也不会太过失礼 :p - Matthieu M.
关于在这里允许未定义的行为,这意味着对于一个未知内容的映射,您必须始终在查找之前先进行检查,以避免它,这可能不是特别明智的使用模式。 - visitor
@Matthieu 我同意当前的行为不是直观的,但它是一致的,因此一旦你找到了它,就不会感到困惑。拥有两个版本的operator [],其中一个保证始终有效,另一个可能在某些情况下引发异常或导致未定义的行为,并且唯一的区别是底层对象是否为const,这将是令人困惑的,因为在其他地方更改变量的const性质可能会大大改变程序的行为,而不会导致任何编译时错误或警告。 - Tyler McHenry
我理解你的观点,实际上我一直觉得 Boost Serialization 有点烦人,因为序列化和反序列化之间唯一的区别就是运算符的常量性... - Matthieu M.

4

operator[]会在map中不存在该条目时创建它。如果operator[]被实现为const map,则这是不可能的。以下是The C++ Programming Language中给出的解释:

当键值未找到时,对map进行下标操作会添加一个默认元素。因此,const map没有operator[]的版本。此外,只有当mapped_type(value type)具有默认值时,才能使用下标操作。如果程序员只想查看是否存在某个键,则可以使用find()操作(§17.4.1.6)来定位键而不修改map。


2

它确实有一个不可变版本,称为find()


1

operator[] 如果找不到键,则会插入,因此它不能是常量成员函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接