我在stackoverflow上阅读了有关网络和问题的不同文章,但对我来说,使用std::map::at
检索地图元素更好是否有任何独家案例。
根据definition,std::map::at
返回对标识为的元素的映射值的引用 关键k。
如果k与容器中任何元素的键不匹配,则 函数抛出out_of_range异常。
仅限于我在100%确定存在具有特定键的元素时值得使用std::map::at
的情况,否则您应该考虑异常处理。
答案 0 :(得分:55)
与此处的大多数现有答案相反,请注意实际上有 4 方法与在地图中查找元素相关(忽略lower_bound
,upper_bound
和{{1} },这不太精确):
equal_range
仅存在于非const版本中,如上所述,如果元素不存在,它将创建元素operator[]
返回对该元素的引用(如果存在)并抛出异常at()
返回元素的迭代器(如果存在)或find()
的迭代器(如果不存在)map::end()
返回此类元素的数量,在count()
中,这是0或1 现在语义清晰,让我们来看看何时使用:
map
(或不是)中是否存在元素,请使用map
。count()
,然后使用map
。at()
中,请使用map
;不要忘记检查生成的迭代器是否不等于find()
的结果。end()
;如果您不希望调用类型默认构造函数来创建它,请适当地使用operator[]
或insert
答案 1 :(得分:21)
std::map::at()
会抛出out_of_range
个异常。此异常是一种logic_error
异常,从使用的角度来看,对我来说是一种assert()
的同义词:它应该用于报告程序内部逻辑中的错误,例如违反逻辑前提条件或类不变量。
此外,您可以使用at()
访问const地图。
所以,对于你的问题:
at()
而不是[]
。map::find()
:在这种情况下,它不是一个逻辑错误,所以抛出和捕获std::logic_error
异常将不是非常优雅的方式编程,即使我们不考虑性能。答案 2 :(得分:12)
如您所述,有三种不同的方式可以访问地图中的元素:at()
,operator[]
和find()
(还有upper_bound
,{{1} }和lower_bound
,但这些是针对更复杂的情况,你可能想要找到下一个/前一个元素等。)
那么,你什么时候应该使用哪一个?
equal_range
基本上是"如果它不存在,用一个默认构造的映射元素"创建一个。这意味着它不会抛出(除非在内存分配抛出或其中一个键或值构造函数抛出的角落情况下),并且您肯定会获得对您查找的元素的引用 - 无论是现有元素还是新创造的。
operator[]
将被抛出。由于您不应该对正常的程序流使用异常,因此使用at()
说"我确信有这样的元素。"但如果你错了,你会得到一个例外(而不是未定义的行为)的额外好处。如果你不肯定该元素存在,请不要使用它。
at()
说"可能有也可能没有这样的元素,让我们看看......"并为您提供以不同方式对两种情况作出反应的可能性。因此,它是更通用的方法。
答案 3 :(得分:5)
find
,operator[]
和at
中的所有3个都很有用。
find
是好的,但只要它们存在就行动。
at
如果您希望某些内容应该出现在地图上并且如果不是这样的话就会抛出异常。它还可以使用比const
更简洁的内容访问find
地图(您无法使用op[]
)
op[]
如果你希望插入一个默认元素,那就好了,例如对于单词计数程序,它为每个遇到的单词放置一个int 0
第一次(使用成语words[word]++;
)。
答案 4 :(得分:3)
这取决于此功能的要求以及您如何构建项目。如果你应该返回一个对象但你不能因为它没有被找到,那么它将为你提供两个如何处理它的选项。你可以通过一个例外,或者你可以返回某种哨兵,这意味着没有找到任何东西。如果要抛出异常,请使用at()
,因为将抛出异常。如果您不想抛出异常,请使用find()
,这样您就不必处理异常只是为了返回一个sentinel对象。
答案 5 :(得分:3)
我认为,这取决于你的用例。返回类型std::map::at()
是对找到的元素的值的左值引用,而std::map::find()
返回迭代器。您可能更喜欢
return myMap.at("asdf"s) + 42;
在更精细的表达中
return myMap.find("asdf"s)->second + 42;
每当在表达式中使用std::map::at()
的结果时,您希望元素存在,并将缺少的元素视为错误。所以异常是处理它的好选择。
答案 6 :(得分:2)
我猜不同之处在于语义。
std::map::at()
在我的机器上看起来像这样:
mapped_type&
at(const key_type& __k)
{
iterator __i = lower_bound(__k);
if (__i == end() || key_comp()(__k, (*__i).first))
__throw_out_of_range(__N("map::at"));
return (*__i).second;
}
如您所见,它使用lower_bound
,然后检查end()
,比较密钥,并在需要时抛出异常。
find()
看起来像这样:
iterator
find(const key_type& __x)
{ return _M_t.find(__x); }
其中_M_t
是存储实际数据的红黑树。显然,两个函数都具有相同的(对数)复杂度。当您使用find()
+检查end()
时,您执行的操作与at
几乎完全相同。我会说语义上的区别是:
at()
,并假设它在那里。在这种情况下,所需位置缺少元素的情况是例外情况,因此at()
会抛出异常。find()
。在这种情况下,元素不存在的情况是正常的。另请注意,find()
返回一个迭代器,您可以将其用于除了获取其值之外的其他目的。答案 7 :(得分:2)
map :: at()返回一个l值引用,当你通过引用返回时,你可以使用它的所有可用的好处,比如方法链。
示例:
map<int,typ> table;
table[98]=a;
table[99]=b;
table.at(98)=table.at(99);
operator[]
也通过引用返回映射值,但是如果找不到密钥,它可能会插入一个值,在这种情况下容器大小会增加一个。
这需要你格外小心,因为你必须照顾iterator invalidation。
我是对的,当有一个时,最好使用map :: find() 有没有这种钥匙元素的可能性?并且map :: find()就是这样 更快更优雅的方法?
是的,从语义上讲,当你不确定是否存在元素时使用find()是有意义的。即使对于新手来说,代码也更容易理解。
至于时间效率,map通常实现为RB树/一些平衡二叉搜索树,因此,find()的复杂度为O(logN)。
C ++规范:
T&安培; operator [](const key_type&amp; x);
效果:如果地图中没有等效于x的键,则将value_type(x,T())插入到地图中。 要求:key_type应为CopyInsertable,而mapped_type应为 DefaultInsertable进入* this。返回:对的引用 mapped_type对应于* in * this。 4复杂性:对数。T&安培; at(const key_type&amp; x);
const T&amp; at(const key_type&amp; x)const; 返回:对* this中x对应的mapped_type的引用。 抛出:如果没有这样的元素,则为out_of_range类型的异常对象。 复杂性:对数。