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

69,515

Solution 1

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

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

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 4

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 5

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.

Share:
69,515
LihO
Author by

LihO

Michal Lihocký I've been quite active here back in 2011-13 when working as C++/C# developer. Since then, I've spent the last 6 years traveling throughout Asia and building web solutions for companies around the globe, using Yii, Laravel, Symfony, CakePHP and recently also VueJS and ReactJS + Redux, which is quite cool stuff. Currently based in Singapore, overlooking the development of private equity investing portal Fundnel.

Updated on December 05, 2020

Comments

  • LihO
    LihO over 3 years

    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[] ?

  • LihO
    LihO about 12 years
    If I don't catch out_of_range exception, then abort() is called.
  • Kiran Kumar
    Kiran Kumar about 12 years
    @LihO: Not necessarily..the try..catch can be present in the method which is calling this method.
  • Bojan Komazec
    Bojan Komazec about 12 years
    +1 I like the explanation of how to separate handling of wrong user's input (input validation; invalid input might be expected so is not regarded as something exceptional)...and bugs in the code (dereferencing iterator that is out of range is exceptional thing)
  • Steve Jessop
    Steve Jessop about 12 years
    If nothing else, 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".
  • LihO
    LihO about 12 years
    So you say that I should use size() + [] when index depends on users input, use assert 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...)
  • Steve Jessop
    Steve Jessop about 12 years
    @LihO: if your implementation offers a debugging implementation of vector then it's probably better to use that as the "just in case" option rather than at() everywhere. That way you can hope for a bit more performance in release mode, just in case you ever need it.
  • Alexandre C.
    Alexandre C. about 12 years
    @SteveJessop: I don't like throwing exceptions for program bugs, since they can be caught upstream by other programmers. Assertions are much more useful here.
  • Steve Jessop
    Steve Jessop about 12 years
    @AlexandreC. well, the official response to that is that out_of_range derives from logic_error, and other programmers "should" know better than to catch logic_errors 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.
  • pmdj
    pmdj about 12 years
    Yeah, most STL implementations these days support a debug mode which bounds-checks even operator[], e.g. gcc.gnu.org/onlinedocs/libstdc++/manual/… so if your platform supports this, you're probably best off going with it!
  • James Kanze
    James Kanze about 12 years
    @phresnel 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.
  • James Kanze
    James Kanze about 12 years
    @phresnel Most of the code I've delivered has been in "debug" mode. You only turn off checking when performance issues actually require it. (Microsoft pre-2010 was a bit of a problem here, since 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.)
  • Sebastian Mach
    Sebastian Mach about 12 years
    I am more of the camp that says "code as sanctioned (guaranteed) by standard"; of course you are free to deliver in debug mode, but when doing cross platform development (including, but not exclusively, the case of same OS, but different compiler versions), relying on the standard is the best bet for releases, and debug mode is considered a tool for the programmer to get that thing mostly correct and robust:)
  • James Kanze
    James Kanze about 12 years
    @phresnel Obviously, you only count on the standard. But if some platform does guarantee a crash for undefined behavior, it would be foolish not to take advantage of it (unless the profiler tells you you can't). You can't never be 100% sure that there is no error in your code, and it's reassuring to know that at least in some specific cases, on some specific platforms, you'll crash, rather than destroy all of the clients data.
  • James Kanze
    James Kanze about 12 years
    @phresnel If there is an issue of performance, then by all means, turn off the checking. This isn't the case for most applications, however.
  • Sebastian Mach
    Sebastian Mach about 12 years
    On the other hand, if the critical parts of your application are isolated and protected by e.g. exception safety (RAII ftw), then should every single access to 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.
  • Sebastian Mach
    Sebastian Mach about 12 years
    out there be dragons. what happens if we click that link? (hint: I know it already, but on StackOverflow we prefer comments that don't suffer link rot, i.e. provide a short summary about what you want to say)
  • ahj
    ahj about 12 years
    Thanks for the tip. It is fixed now.
  • dolphin
    dolphin almost 10 years
    ...especially when you need a pointer to the n-th element of a vector.
  • underscore_d
    underscore_d about 8 years
    @pmdj fantastic point, which i didn't know about... but orphaned link. :P current one is: gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html
  • Big Temp
    Big Temp almost 4 years
    Exceptions in C++ are meant to be an error handling mechanism, not a tool for debugging. Herb Sutter explains why throwing std::out_of_range or any form of std::logic_error is, in fact, a logic error in and of itself here.
  • Shital Shah
    Shital Shah over 3 years
    @BigTemp - I'm not sure how your comment related to this question and answer. Yes, exceptions are highly debated topic but the question here is the difference between 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.
  • einpoklum
    einpoklum over 3 years
    That's not a good enough reason, IMHO, to prefer 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.
  • Marcel Ferrari
    Marcel Ferrari about 3 years
    I know this is quite late but id like to add that operator[] and at() have a very noticeable performance difference! I've had code not pass our university's auto-grader timed tests because of at()... simply replacing all calls with operator[] made the code run quick enough to pass all tests.
  • Marcel Ferrari
    Marcel Ferrari about 3 years
    @einpoklum I've had code not pass our university's auto-grader timed tests because of 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.
  • Zitrax
    Zitrax over 2 years
    I also think that it's better to use 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.