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

C++StlStdvector

C++ Problem Overview


I know that at() is slower than [] because of its boundary checking, which is also discussed in similar questions like https://stackoverflow.com/q/2578638/1168156 or https://stackoverflow.com/q/3269809/1168156. I just don't understand what the at() method is good for.

If I have a simple vector like this one: 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:

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:

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

So my question is:
What are advantages of using vector::at over vector::operator[] ?
When should I use vector::at rather than vector::size + vector::operator[] ?

C++ Solutions


Solution 1 - C++

I'd say the exceptions that vector::at() throws aren't really intended to be caught by the immediately surrounding code. They are mainly useful for catching bugs in your code. If you need to bounds-check at runtime because e.g. the index comes from user input, you're indeed best off with an if statement. 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. (just like an assert())

Solution 2 - C++

> it forces me to wrap it with try-catch block

No it doesn't (the try/catch block can be upstream). 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). 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.

Solution 3 - C++

> What are advantages of using vector::at over vector::operator[] ? > When should I use vector::at rather than 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. 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.

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().

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. 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.

Solution 4 - C++

at can be clearer if you have a pointer to the vector:

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

Performance aside, the first of these is the simpler and clearer code.

Solution 5 - C++

In debug builds, it is not guaranteed for at() to be slower than 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. (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.)

In practice, at() is rarely appropriate. If the context is such that you know the index may be invalid, you probably want the explicit test (e.g. 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.

Solution 6 - C++

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. Now the XML tree is a tree, so your probably want to use recursion to analyze it. Deep down, in the recursion, there might be an access violation by the writer of the XML file. 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. 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. 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.

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().

Solution 7 - C++

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. Otherwise, if access is just based on the capacity of the vector it is safer to use at.

Solution 8 - C++

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. 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. 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. Personally, I try to use at because I don’t want a nasty bug to create undefined behavior and sneak in to production.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLihOView Question on Stackoverflow
Solution 1 - C++pmdjView Answer on Stackoverflow
Solution 2 - C++Alexandre C.View Answer on Stackoverflow
Solution 3 - C++Tony DelroyView Answer on Stackoverflow
Solution 4 - C++BrangdonView Answer on Stackoverflow
Solution 5 - C++James KanzeView Answer on Stackoverflow
Solution 6 - C++ltjaxView Answer on Stackoverflow
Solution 7 - C++ahjView Answer on Stackoverflow
Solution 8 - C++Shital ShahView Answer on Stackoverflow