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!! 5 to 10 times slower/faster!. 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[] ?
if (i < v.size()) v[i] = 2;
, there's a possible code path that doesn't assign 2
to any element of v
at all. If that's the correct behavior, great. But often there's nothing sensible that this function can do when i >= v.size()
. So there's no particular reason why it shouldn't use an exception to indicate an unexpected situation. Many functions just use operator[]
without a check against the size, document that i
must be in range, and blame the resulting UB on the caller.
at
is safer. For example, given an obj
vector with 100 elements. The obj.at(143) = 69;
explodes right away. Whereas, obj[143] = 69;
will silently creep in without your notice.
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()
)
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.
out_of_range
exception, then abort()
is called.
try..catch
can be present in the method which is calling this method.
at
is useful to the extent that you would otherwise find yourself writing something like if (i < v.size()) { v[i] = 2; } else { throw what_are_you_doing_you_muppet(); }
. People often think of exception-throwing functions in terms of "curses, I have to handle the exception", but as long as you carefully document what each of your functions can throw, they can also be used as "great, I don't have to check a condition and throw an exception".
out_of_range
derives from logic_error
, and other programmers "should" know better than to catch logic_error
s upstream and ignore them. assert
can be ignored too if your colleagues are keen not to know about their mistakes, it's just harder because they have to compile your code with NDEBUG
;-) Each mechanism has its merits and flaws.
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.
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.
at()
. Just write: auto& vector = *pVector;
and now you can do return vector[n]
. Also, you should really avoid working through pointers (as opposed to references) directly, especially to complex classes.
at()
... simply replacing all calls with operator[]
made the code run quick enough to pass all tests. at()
and operator[]
have a very noticeable performance difference.
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.
operator[]
isn't required to do bounds checking, but all good implementations do. At least in debugging mode. The only difference is what they do if the index is out of bounds: operator[]
aborts with an error message, at()
throws an exception.
std::string
didn't always work if the checking options didn't correspond with those of the runtime: -MD
, and you'd better turn off checking, -MDd
, and you'd better have it on.)
operator[]
be crippled? E.g., std::vector<color> surface(witdh*height); ...; for (int y=0; y!=height; ++y)...
. I think enforcing bounds checking on delivered binaries falls under premature pessimization. Imho, it should only be band aid for not-well-engineered code.
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()
.
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
.
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.
std::out_of_range
or any form of std::logic_error
is, in fact, a logic error in and of itself here.
at
and []
and my answer simply states the difference. I personally use "safe" method when perf is not an issue. As Knuth says don't do premature optimization. Also, it's good to get bugs caught early than in production regardless of philosophical differences.
at
as long as it's not in a very performance sensitive part of the code. It's so much better to get an exception thrown immediately instead of the program continuing with bogus data which might cause much worse problems than a non noticeable performance difference.
Success story sharing
size()
+[]
when index depends on users input, useassert
in situations where index should never be out of bounds for easy bug fixing in the future and.at()
in all other situations (just in case, cause something wrong might happen...)vector
then it's probably better to use that as the "just in case" option rather thanat()
everywhere. That way you can hope for a bit more performance in release mode, just in case you ever need it.operator[]
, e.g. gcc.gnu.org/onlinedocs/libstdc++/manual/… so if your platform supports this, you're probably best off going with it!