在我看来,“邪恶”(在C ++ FAQ意义上),对于通常用于访问数据结构的运算符,突然被定义为将数据插入到数据结构中。
我猜问题是'什么会更好'?对于某些类型的映射值,可以轻松回答这个问题;例如,如果我们将键映射到指针,您可能真的希望operator []为不存在的键返回nullptr,但这显然不适用于其他类型。
它可能会在不存在的键上抛出异常,甚至默认构造一个临时的并返回它而不将其添加到地图中。将[]从读取语义转换为为此容器类型编写语义的好理由是什么?
答案 0 :(得分:12)
基本问题是没有可靠区分的句法方法:
dosomething(collection[foo]);
来自
collection[foo] = something;
在运营商的定义中。因为它可能出现在任一位置,所以该类确保它可以处理两者,如果需要,提供默认覆盖。如果您发现这是不合情理的,那么您需要完全避免std::map::operator[]
。
另一个原因是,当密钥不在列表中时,必须有一些已定义的行为。由于operator[]
必须返回一个值(LValue或RValue),因此它不能返回空指针,past-the-end迭代器或任何其他sentinel值。唯一剩下的选择是提出例外。 STL不会引发很多异常,因为即使在没有异常的情况下也可以使用它。必须选择其他一些行为,这就是结果。
解决此问题的最佳方法是使用没有此行为的std::map
成员函数。那将是map::find()
,如果找不到密钥则返回map::end
。
答案 1 :(得分:2)
“将[]从读取语义转换为为此容器类型编写语义的好理由是什么?”
考虑了一段时间后,我可以想到两个原因。第一个原因是效率。它有助于反思实际算法以及语义是否使生活更轻松或更难。当前语义闪耀的一种算法是累积与键相关联的值。
void f(std::vector<std::pair<std::string, double> > const& v)
{
std::map<std::string, double> count;
for (size_t i = 0, sz = v.size(); i < sz; ++i) {
count[v[i].first] += v[i].second;
}
}
在这种情况下,映射语义很好,因为你可以依赖于初始化为零的计数中的每个值,这可能是你想要的。在这种情况下,我们只对地图中的每个键和值对进行一次搜索。
如果将其与Python进行比较(如果您建议的密钥不存在则抛出异常),您会看到更加混乱且效率低下的代码:
def f(vec):
count = {}
for (k, v) in vec:
if count.has_key(k):
count[k] += v
else:
count[k] = v
或使用get()和默认值的稍微整洁的版本。
def g(vec):
count = {}
for (k, v) in vec:
count[k] = count.get(k, 0) + v
return count
请注意,在这两个版本中,对每个键和值对执行两次字典搜索。根据您的要求,这可能是一个严重的惩罚。因此,在这种情况下,C ++映射语义对于高效代码是必需的。
C ++有const,这是保护事物不被改变的绝佳设施。我有时怀疑const被大大低估了。在您的情况下,使用const将保护您不使用operator []更改地图的内容。
这种行为的第二个好理由是它与许多语言中的关联数组的行为相同。像Awk和Perl这样的语言几十年来一直对关联数组有相同的行为。如果你来自这些语言,std :: map的行为可能非常直观。
答案 2 :(得分:1)
正如TokenMacGuy所提到的,:: operator []的用法可能含糊不清,你所描述的是他们处理歧义的方式。
作为一名软件开发人员,重要的一点是,第三方库几乎从未按照您使用它们的方式完全编写。更糟糕的是,设计不良的第三方库可能会损害您的代码质量。
你刚才描述的所有内容都可以通过抽象std :: map类来轻松完成,如果:: operator []的副作用让你烦恼,我会鼓励你把它抽象出去。
答案 3 :(得分:0)
要了解关于STL的大多数设计决策,需要先了解它的演变,特别是在SGI STL。
map
是Pair Associative Container和Unique Sorted Associative Container的模型。
第二个概念很重要,因为它与set
共享,按键访问(使用operator[]
)只是没有意义。
insert
的语义(不替换值)被设计为适合map
和set
(使其可以分解它们的实现)。请注意,对于set
,其元素是不可变的,替换也没有意义。
因此,我认为,与介绍operator[]
时相比,选择了这种奇怪的语义,以便为insert
和map
替代multimap
。稍微复杂一点:
map.insert(std::make_pair(key, Value())).first->second = value;
老实说,我希望它的行为更像find
(std::key_error
)。这种语义肯定不直观。