c++ 当我应该使用std::map::at来检索map元素时

z9ju0rcb  于 2023-10-20  发布在  其他
关注(0)|答案(8)|浏览(122)

我在网上读过不同的文章,也在stackoverflow上问过问题,但对我来说,不清楚是否有任何排他性的情况下,使用std::map::at来检索Map元素会更好。
根据definitionstd::map::at
返回一个引用,指向用键k标识的元素的Map值。
如果k与容器中任何元素的键都不匹配,函数将抛出out_of_range异常。
对于我来说,只有当你100%确定具有特定键的元素存在时,才值得使用std::map::at,否则你应该考虑异常处理。
1.是否有任何情况下,std::map::at被认为是最有效和优雅的方式?在什么情况下你会推荐使用std::map::at
1.当有可能没有带有这样一个键的元素时,我是不是更好地使用map::find()?而map::find()是不是更快更优雅的方法呢?

if ( map.find("key") != map.end() )
{
    // found 

} else
{
    // not found
}

p.s

map::operator[]有时候会很危险,因为如果一个元素不存在,它就会插入它。

**编辑:**链接以某种方式相关link 1link 2link 3link 4link 5link 6

zlhcx6iw

zlhcx6iw1#

与大多数现有的答案相反,请注意,实际上有4种方法与查找map中的元素有关(忽略lower_boundupper_boundequal_range,它们不太精确):

  • operator[]只存在于非const版本中,如前所述,如果不存在,它将创建元素
  • at(),在C++11中引入,如果元素存在,则返回对元素的引用,否则抛出异常
  • 如果元素存在,find()返回元素的迭代器;如果元素不存在,则返回map::end()的迭代器
  • count()返回此类元素的数量,在map中,这是0或1
  • contains()(从C++20开始)返回元素是否存在。

现在语义已经清楚了,让我们回顾一下何时使用哪个:

  • 如果你只想知道一个元素是否存在于map中,那么使用contains()(从C++20开始)或count()
  • 如果你想访问这个元素,并且它应该在map中,那么使用at()
  • 如果你想访问元素,但不知道它是否在map中,那么使用find();不要忘记检查结果迭代器不等于end()的结果。
  • 最后,如果您希望访问存在的元素,或者创建(并访问)不存在的元素,请使用operator[];如果不希望调用类型默认构造函数来创建它,则可以适当地使用insertemplace
qybjjes1

qybjjes12#

std::map::at()在找不到元素时抛出out_of_range异常。这个异常是一种logic_error异常,对我来说,从使用的Angular 来看,它是assert()的同义词:它应该用来报告程序内部逻辑中的错误,比如违反逻辑前提或类不变量。
此外,您可以使用at()访问constMap。
所以,对于你的问题:
1.当访问constMap和元素缺失是逻辑错误时,我建议使用at()而不是[]
1.是的,当你不确定元素是否在这里时,最好使用map::find():在这种情况下,它不是逻辑错误,因此抛出和捕获std::logic_error异常不是非常优雅编程方式,即使我们不考虑性能。

kse8i1jr

kse8i1jr3#

如您所述,有三种不同的方式可以访问map中的元素:at()operator[]find()(也有upper_boundlower_boundequal_range,但这些是针对更复杂的情况,你可能想找到下一个/上一个元素等)
那么,什么时候应该使用哪一个呢?
operator[]基本上是“如果它不存在,则创建一个具有默认构造的Map元素”。这意味着它不会抛出(除了在内存分配抛出或键或值构造函数抛出的极端情况下),并且您肯定会获得对所查找元素的引用-现有元素或新创建的元素。
at()如果该键没有元素则抛出。因为你不应该在正常的程序流中使用异常,所以使用at()就像是在说“我确定有这样的元素”,但是如果你错了,你会得到一个异常(而不是未定义的行为)。如果你不确定元素是否存在,不要使用这个。
find()表示“可能有也可能没有这样的元素,让我们看看......”,并为您提供了对两种情况做出不同React的可能性。因此,这是一种更普遍的方法。

qgelzfjb

qgelzfjb4#

findoperator[]at都是有用的。

  • find是很好的,如果你不想意外插入元素,而只是在它们存在的情况下采取行动。
  • at很好,如果你期望某个东西应该在Map上,如果它不是,你会抛出一个异常。它还可以以比find更简洁的方式访问constMap(不能使用op[]
  • op[]是很好的,如果你想插入一个默认的元素,例如单词计数程序,它把一个int 0的每一个第一次遇到的单词(与习惯用法words[word]++;)。
czfnxgou

czfnxgou5#

这取决于此功能的需求以及您如何构建项目。如果你应该返回一个对象,但你不能,因为它没有找到,那么它留给你两个选择,如何处理。你可以通过一个异常,或者你可以返回某种sentinel,这意味着什么都没有找到。如果你想抛出一个异常,那么就使用at(),因为这个异常会被抛出。如果你不想抛出异常,那么使用find(),这样你就不必为了返回一个sentinel对象而处理异常。

ccgok5k5

ccgok5k56#

我认为,这取决于你的使用情况。std::map::at()的返回类型是对找到的元素的值的左值引用,而std::map::find()返回迭代器。你可能更喜欢

return myMap.at("asdf"s) + 42;

在表达上,

return myMap.find("asdf"s)->second + 42;

每当在表达式中使用std::map::at()的结果时,您都希望该元素存在,并将缺少的元素视为错误。所以异常是处理这个问题的好选择。

oxcyiej7

oxcyiej77#

我想区别在于语义。
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() + check for end()时,您所做的事情与at几乎相同。我会说语义上的区别是:

  • 当你需要一个元素在一个特定的位置时,使用at(),你假设它在那里。在这种情况下,元素从所需位置丢失的情况是异常的,因此at()抛出异常。
  • 如果需要在map中查找元素,请使用find()。在这种情况下,元素不存在的情况是正常的。还要注意,find()返回一个迭代器,您可以将其用于除简单获取其值之外的其他目的。
l7wslrjt

l7wslrjt8#

map::at()返回一个左值引用,当你通过引用返回时,你可以使用它的所有优点,比如方法链。
举例说明:

map<int,typ> table;
  table[98]=a;
  table[99]=b;

  table.at(98)=table.at(99);

operator[]也通过引用返回Map的值,但是如果没有找到键,它可能会插入一个值,在这种情况下容器大小增加1。
这需要你格外小心,因为你必须照顾iterator invalidation
当有可能没有带有这样一个键的元素时,我是否正确地使用map::find()更好?而map::find()是不是更快更优雅的方法呢?
是的,从语义上讲,当你不确定元素的存在时,使用find()是有意义的。即使对于新手来说,这也使代码更容易理解。
至于时间效率,map通常被实现为RB树/某种平衡二叉搜索树,因此find()的复杂度为O(logN)。
C++规格:
int []; int [];
效果:如果map中没有与x等价的键,则将value_type(x,T())插入到map中。要求:key_type必须是CopyInsertable,mapped_type必须是DefaultInsertable into *this。返回:对 *this中x对应的mapped_type的引用。4复杂性:是对数。
T& at(const key_type& x);
const T& at(const key_type& x)const;返回:对 *this中x对应的mapped_type的引用。Throws:一个类型为out_of_range的异常对象,如果不存在这样的元素.复杂性:是对数。

相关问题