简体   繁体   English

vector::at 与 vector::operator[]

[英]vector::at vs. vector::operator[]

I know that at() is slower than [] because of its boundary checking, which is also discussed in similar questions like C++ Vector at/[] operator speed or ::std::vector::at() vs operator[] << surprising results!!我知道at()[]慢,因为它的边界检查,这也在类似的问题中讨论过,比如C++ Vector at/[] operator speed::std::vector::at() vs operator[] < <令人惊讶的结果!! 5 to 10 times slower/faster!慢/快 5 到 10 倍! . . I just don't understand what the at() method is good for.我只是不明白at()方法有什么用。

If I have a simple vector like this one: std::vector<int> v(10);如果我有一个像这样的简单向量: std::vector<int> v(10); and I decide to access its elements by using at() instead of [] in situation when I have a index i and I'm not sure if its in vectors bounds, it forces me to wrap it with try-catch block :我决定使用at()而不是[]来访问它的元素,当我有一个索引i并且我不确定它是否在向量范围内时,它迫使我用 try-catch 块包装它

try
{
    v.at(i) = 2;
}
catch (std::out_of_range& oor)
{
    ...
}

although I'm able to do the get the same behaviour by using size() and checking the index on my own, which seems easier and much convenient for me:虽然我可以通过使用size()并自行检查索引来获得相同的行为,但这对我来说似乎更容易和方便:

if (i < v.size())
    v[i] = 2;

So my question is:所以我的问题是:
What are advantages of using vector::at over vector::operator[] ?使用vector::at比使用vector::operator[] 有什么优势?
When should I use vector::at rather than vector::size + vector::operator[] ?我什么时候应该使用vector::at而不是vector::size + vector::operator[]

I'd say the exceptions that vector::at() throws aren't really intended to be caught by the immediately surrounding code.我想说vector::at()抛出的异常并不是真的要被周围的代码捕获。 They are mainly useful for catching bugs in your code.它们主要用于捕获代码中的错误。 If you need to bounds-check at runtime because eg the index comes from user input, you're indeed best off with an if statement.如果您需要在运行时进行边界检查,因为例如索引来自用户输入,那么您确实最好使用if语句。 So in summary, design your code with the intention that vector::at() will never throw an exception, so that if it does, and your program aborts, it's a sign of a bug.因此,总而言之,设计您的代码时应确保vector::at()永远不会抛出异常,因此如果抛出异常,并且您的程序中止,则表明存在错误。 (just like an assert() ) (就像一个assert()

it forces me to wrap it with try-catch block它迫使我用 try-catch 块包装它

No it doesn't (the try/catch block can be upstream).不,它没有(try/catch 块可以在上游)。 It is useful when you want an exception to be thrown rather than your program to enter undefined behavior realm.当您希望抛出异常而不是您的程序进入未定义的行为领域时,它很有用。

I agree that most out of bounds accesses to vectors are a programmer's mistake (in which case you ought to use assert to locate those mistakes more easily; most debug versions of standard libraries do this automatically for you).我同意对向量的大多数越界访问是程序员的错误(在这种情况下,您应该使用assert来更轻松地定位这些错误;标准库的大多数调试版本会自动为您执行此操作)。 You do not want to use exceptions that can be swallowed upstream to report programmer mistakes: you want to be able to fix the bug .您不想使用可以被上游吞噬的异常来报告程序员错误:您希望能够修复错误

Since it is unlikely that an out of bounds access to a vector is part of the normal program flow (in the case it is, you're right: check beforehand with size instead of letting the exception bubble up), I agree with your diagnostic: at is essentially useless.由于对向量的越界访问不太可能是正常程序流程的一部分(在这种情况下,您是对的:事先检查size而不是让异常冒泡),我同意您的诊断: at基本上没用。

at can be clearer if you have a pointer to the vector:如果您有指向向量的指针,则at会更清楚:

return pVector->at(n);
return (*pVector)[n];
return pVector->operator[](n);

Performance aside, the first of these is the simpler and clearer code.撇开性能不谈,首先是更简单、更清晰的代码。

What are advantages of using vector::at over vector::operator[] ?使用 vector::at 比使用 vector::operator[] 有什么优势? When should I use vector::at rather than vector::size + vector::operator[] ?我什么时候应该使用 vector::at 而不是 vector::size + vector::operator[] ?

The important point here is that exceptions allow separation of the normal flow of code from the error handling logic, and a single catch block can handle problems generated from any of myriad throw sites, even if scattered deep within function calls.这里的重点是异常允许将正常的代码流与错误处理逻辑分离,并且单个 catch 块可以处理由无数抛出站点中的任何一个产生的问题,即使它们分散在函数调用的深处。 So, it's not that at() is necessarily easier for a single use, but that sometimes it becomes easier - and less obfuscating of normal-case logic - when you have a lot of indexing to validate.因此,并不是说at()单次使用就一定更容易,而是有时它会变得更容易 - 并且对正常情况逻辑的混淆更少 - 当您有很多索引要验证时。

It's also noteworthy that in some types of code, an index is being incremented in complex ways, and continually used to look up an array.同样值得注意的是,在某些类型的代码中,索引以复杂的方式递增,并不断用于查找数组。 In such cases, it's much easier to ensure correct checks using at() .在这种情况下,使用at()确保正确检查要容易得多。

As a real-world example, I have code that tokenises C++ into lexical elements, then other code that moves an index over the vector of tokens.作为一个真实的例子,我有将 C++ 标记为词法元素的代码,然后是将索引移动到标记向量上的其他代码。 Depending on what's encountered, I may wish to increment and check the next element, as in:根据遇到的情况,我可能希望增加并检查下一个元素,如下所示:

if (token.at(i) == Token::Keyword_Enum)
{
    ASSERT_EQ(tokens.at(++i), Token::Idn);
    if (tokens.at(++i) == Left_Brace)
        ...
    or whatever

In this kind of situation, it's very hard to check whether you've inappropriately reached the end of the input because that's very dependent on the exact tokens encountered.在这种情况下,很难检查您是否不恰当地到达了输入的末尾,因为这非常依赖于遇到的确切标记。 Explicit checking at each point of use is painful, and there's much more room for programmer error as pre/post increments, offsets at the point of use, flawed reasoning about the continued validity of some earlier test etc. kick in.在每个使用点进行显式检查是痛苦的,并且随着前/后增量、使用点的偏移、关于某些早期测试的持续有效性的错误推理等,程序员错误的空间更大。

In debug builds, it is not guaranteed for at() to be slower than operator[] ;在调试版本中,不能保证at()operator[]慢; I'd expect them to be about the same speed.我希望它们的速度大致相同。 The difference is that at() specifies exactly what will happen in there is a bounds error (an exception), where as in the case of operator[] , it is undefined behavior — a crash in all of the systems I use (g++ and VC++), at least when the normal debugging flags are used.不同之处在于at()准确地指定了边界错误(异常)时会发生什么,而在operator[]的情况下,它是未定义的行为——我使用的所有系统(g++ 和VC++),至少在使用正常调试标志时。 (Another difference is that once I'm sure of my code, I can get a substantial speed increase for operator[] by turning the debugging off. If the performance requires it — I wouldn't do it unless it were necessary.) (另一个区别是,一旦我确定我的代码,我就可以通过关闭调试来显着提高operator[]的速度。如果性能需要它 - 除非有必要,否则我不会这样做。)

In practice, at() is rarely appropriate.实际上, at()很少是合适的。 If the context is such that you know the index may be invalid, you probably want the explicit test (eg to return a default value or something), and if you know that it can't be invalid, you want to abort (and if you don't know whether it can be invalid or not, I'd suggest that you specify your function's interface more precisely).如果上下文使您知道索引可能无效,您可能需要显式测试(例如返回默认值或其他内容),并且如果您知道它不能无效,您想中止(如果你不知道它是否可以无效,我建议你更精确地指定你的函数接口)。 There are a few exceptions, however, where the invalid index may result from parsing user data, and the error should cause an abort of the entire request (but not bring the server down);但是,有一些例外,其中无效索引可能是由于解析用户数据而导致的,并且该错误应该导致整个请求中止(但不会使服务器停机); in such cases, an exception is appropriate, and at() will do that for you.在这种情况下,例外是合适的,而at()将为您做到这一点。

Note: It appears some new folks are downvoting this answer without having courtesy of telling what is wrong.注意:似乎有些新人在没有礼貌地说出错误的情况下对这个答案投反对票。 Below answer is correct and can be verified here .下面的答案是正确的,可以在这里验证。

There is really only one difference: at does bounds checking while operator[] doesn't.实际上只有一个区别: at边界检查而operator[]没有。 This applies to debug builds as well as release builds and this is very well specified by the standards.这适用于调试版本和发布版本,并且这在标准中有很好的规定。 It's that simple.就这么简单。

This makes at a slower method but it's also really bad advice to not to use at .这使得at方法变慢,但不使用at也是非常糟糕的建议。 You have to look at absolute numbers, not relative numbers.你必须看绝对数字,而不是相对数字。 I can safely bet that most of your code is doing fat more expensive operations than at .我可以肯定地打赌,您的大部分代码都在执行比at更昂贵的操作。 Personally, I try to use at because I don't want a nasty bug to create undefined behavior and sneak in to production.就我个人而言,我尝试使用at是因为我不希望一个讨厌的错误创建未定义的行为并潜入生产环境。

The whole point of using exceptions is that your error handling code can be further away.使用异常的全部意义在于您的错误处理代码可以更远。

In this specific case, user input is indeed a good example.在这种特定情况下,用户输入确实是一个很好的例子。 Imagine you want to semantically analyze an XML data-structure which uses indices to refer to some kind of resource you internally store in a std::vector .想象一下,您想从语义上分析一个 XML 数据结构,该结构使用索引来引用您内部存储在std::vector中的某种资源。 Now the XML tree is a tree, so your probably want to use recursion to analyze it.现在 XML 树是一棵树,所以您可能想使用递归来分析它。 Deep down, in the recursion, there might be an access violation by the writer of the XML file.在深层,在递归中,XML 文件的作者可能存在访问冲突。 In that case, you usually want to bump out of all the levels of recursion and just reject the whole file (or any kind of "coarser" structure).在这种情况下,您通常希望跳出所有级别的递归并拒绝整个文件(或任何类型的“粗略”结构)。 This is where at comes in handy.这就是 at 派上用场的地方。 You can just write the analysis code as-if the file was valid.您可以像文件有效一样编写分析代码。 The library code will take care of the error detection and you can just catch the error on the coarse level.库代码将负责错误检测,您可以在粗略级别上捕获错误。

Also, other containers, like std::map , also have std::map::at which has slightly different semantics than std::map::operator[] : at can be used on a const map, while operator[] cannot.此外,其他容器,如std::map ,也有std::map::at ,它的语义与std::map::operator[]略有不同:at 可用于 const 映射,而operator[]不能. Now if you wanted to write container agnostic code, like something that could deal with either const std::vector<T>& or const std::map<std::size_t, T>& , ContainerType::at would be your weapon of choice.现在,如果您想编写与容器无关的代码,例如可以处理const std::vector<T>&const std::map<std::size_t, T>&ContainerType::at将是您的武器选择。

However, all these cases usually appear when handling some kind of unvalidated data-input.但是,在处理某种未经验证的数据输入时,通常会出现所有这些情况。 If you are sure about your valid range, as you usually should be, you can usually use operator[] , but better yet, iterators with begin() and end() .如果您确定自己的有效范围,通常应该是这样,您通常可以使用operator[] ,但更好的是,使用begin()end()迭代器。

According to this article, performance aside, it doesn't make any difference to use at or operator[] , only if the access is guaranteed to be within the size of the vector.根据这篇文章,抛开性能不谈,使用atoperator[]没有任何区别,仅当访问保证在向量的大小范围内时。 Otherwise, if access is just based on the capacity of the vector it is safer to use at .否则,如果访问仅基于向量的容量,则使用at更安全。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM