C++ 为什么可以';对于map,我们有一个不可变的操作符[]

C++ 为什么可以';对于map,我们有一个不可变的操作符[],c++,stl,C++,Stl,以下代码可以正常工作: std::map<int, int>& m = std::map<int, int>(); int i = m[0]; std::map无法提供 const mapped_type& operator[](const key_type& _Keyval) const 运算符[]在找不到键时插入,因此它不能是const成员函数。原因是std::map语义表示,如果尝试访问不存在的键处的元素,则会使用默认构造的元素创建键。换

以下代码可以正常工作:

std::map<int, int>& m = std::map<int, int>();
int i = m[0];
std::map无法提供

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

运算符[]在找不到键时插入,因此它不能是const成员函数。

原因是
std::map
语义表示,如果尝试访问不存在的键处的元素,则会使用默认构造的元素创建键。换句话说,
m[0]
将在位置0处创建一个int(如果还不存在)。显然,这与常量映射不兼容

你可以说“好吧,制作一个const版本的
操作符[]
,让它不要这样做!”,但有两个问题:语义上的差异不明显且令人困惑,而且如果你试图访问一个不存在的密钥(抛出异常?),具体会发生什么情况还不清楚


相反,您应该在映射上使用
find()
方法,该方法将返回一个迭代器,指向您正在查找的键/值对。查找与
操作符[]
一样高效,它可以在常量映射上使用(在这种情况下返回常量迭代器),如果键不存在,它将返回
end()
迭代器

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

运算符[]将创建映射中不存在的条目。如果运算符是为常量映射实现的,则这是不可能的。这是在中给出的解释:

订阅地图会添加默认值 元素,当找不到键时。 因此,常量映射没有运算符[]的版本。 此外,订阅可以是 仅当映射的_类型(值)为 类型)具有默认值。如果 程序员只是想看看 键存在,则执行find()操作 (§17.4.1.6)可用于定位 键而不修改地图


既然罗杰·佩特(Roger Pate)纠正了我(现已删除)的错误答案,我发现这个问题更有趣——为什么不使用
const
版本的
operator[]
,如果条目不在地图上,它会抛出(或有未定义的行为)?@Michael:我完全同意。我从来没有想过“只要使用查找和检查!”是一个很好的答案;更笨拙
at
vector
中抛出异常,为什么不能
操作符[]const
map
中抛出异常?我不确定是否订阅
混淆
参数,因为正如该用户所演示的,它既混乱又混乱。而
map.find(key)->second
不如
map[key]
好。由于所有的
未定义的
行为都已经存在,还有一个不是那么疏忽:这里允许未定义的行为,这意味着对于未知内容的映射,您必须在查找之前先检查以避免它,这可能不是一个特别聪明的使用模式。@Matthieu我同意当前的行为不是直观的,但它是一致的,因此一旦你发现了它是什么,就不会令人困惑。有两个版本的
运算符[]
,其中一个保证始终工作,另一个在某些情况下可能引发异常或导致未定义的行为,唯一的区别是底层对象是否为常量,因为在别处更改变量的常量可能会极大地改变程序的行为,而不会导致任何编译时错误或警告。我理解你的观点,实际上,我总是觉得
Boost Serialization
有点烦人,因为序列化和反序列化之间的唯一区别是运算符的常量。。。
mapped_type& operator[](const key_type& _Keyval)
const mapped_type& operator[](const key_type& _Keyval) const