C++'s container vector
, deque
, ... provide the at(index)
accessor function in addition to operator[index]
to access container elements.
The difference between this member function and member operator function operator[] is that deque::at signals if the requested position is out of range by throwing an out_of_range exception.
I have never, ever needed this function in my code, as it never makes sense in my C++ code to access elements that are possibly out-of-range. The code is always written to access correct indexes (or produce a meaningful error/exception in case indexes can't be made to match.)
I would be interested in real world examples (possibly from some open source project, as that would add some开发者_开发问答 context) where at()
is used in production code.
Maybe someone can give an example of an algorithmic problem where using at()
made sense.
Note: I have recently used it in some unit-test code, where adding index checking code wasn't considered worth the trouble and the out_of_range exception thrown by at()
is considered enough info+context in case the test breaks.
Note: Regarding this answer by ildjarn - I do not want to start a discussion or comment war on this. I am interesting in "positive" finds, that is concrete examples where it has been used. Thank you.
Well, when you don't control the index being used (such as if it's passed in by a client of your code), you should either check it to see if it's in range manually, or use at
to get an exception reported (which you can capture and notify the caller with your own error reporting or simply propagate the standard exception upwards).
In other words, it's the responsibility of the called function to check input parameters but whether it does this explicitly with an if
statement or implicitly by using at
instead of []
is a matter for debate. If all I'm going to do is throw an out_of_range
exception anyway (if the passed in index is greater than or equal to the size of the collection), I think I'll just let at
do it and save myself some coding.
Passing back bad data silently is almost never the best solution. The trouble with simply passing back x[7] for a four-element integer deck is that the caller thinks it's a valid zero. That's not the case.
In my opinion, at()
is a 100% useless member function. Accessing only within the valid bounds of a standard library container is a precondition of using that container, and violations of any precondition should be handled with an assert
rather than by throwing an exception. The existence of at()
in no way helps a container maintain its preconditions/invariants, and in fact only confuses the issue by making proper bounds-checked access appear to not be a precondition.
I.e., throwing an exception for something that can ultimately only be caused by programmer error is beyond silly. See this thread for a more detailed explanation, specifically the posts by D. Abrahams; long though it may be, it's absolutely worth the read: comp.lang.c++.moderated: Exceptions.
EDIT: To clarify in response to the OP's added note, I'm saying that in my experience with C++ -- professionally, open-source, and otherwise -- I've never come across a use of standard containers' at()
, and maintain that it is in fact not used in production code. Further comments or elaboration were merely to rationalize why I think that's the case.
One use case I've consistently found at()
useful in is facilitating parsing of complex user input. For example, in analysing C++ code, I find myself moving along an array of lexical tokens as I check for grammatical constructs. The logic is often like "if this token is an Identifier and the next one is an Equals then it should be an assignment so scan ahead for a Semicolon token to establish the token range for the expression". Using at()
in such code means you can easily express expectations at some offset from the current point, ala:
if (tokens.at(i) == Switch)
{
if (tokens.at(++i) != Left_Parentheses)
// throw or return to say input program's broken...
if (tokens.at(++i) == ...)
...
}
You get an exception whenever you try to parse an invalid program. Incrementing the position happens in so many places throughout the code that reverifying the size continually would be a nightmare (verbose and extremely error prone), as in this kind of situation you only realise how much bigger the program needs to be to be valid as the grammatical rules are applied. Using at()
here is concise, robust, intuitive, and reasonably performant compared to functional equivalent alternatives.
FWIW - a quick search of our production code (200k lines, most written before I joined the team) found a dozen uses of at()
.
My case would rather be: why not use it ?
Unless you are in a performance critical part of your application, you should always favor std::out_of_range
against Undefined Behavior, at least that's my credo.
In practice, I generally transform all the code I am working on to use checked accesses. The performance penalty is invisible for most of the code, and at least I've got a nice report with information on the current context of execution (generated in the catch(std::exception const&)
at the root level) rather than a memory corruption that makes my code fail some times later (or worse, looks like it worked).
I agree that input should be validated first and foremost, I agree that you should check your access beforehand... but just in case you've forgotten or had a bug, better have a at()
.
Using []
instead of at()
is like carrying a loaded gun without/with (resp.) the security on in your pocket. You can forget to put it on, but willingly removing it ? That's insanity.
After a quick search, I found that, among others, Inkscape (the svg editor), Google v8, Android, Chromium, and Ogre used this function. This (rudimentary) list was taken from a simple google search, using the regular expression at\([0-9]+\)
.
Using \.at\([0-9a-z_]+\)
instead of the previous expression gives more generic results, and adds OpenJdk and a wealth of sourceforge projects.
v.at(-1)
won't fail like v[-1]
(you'll get an exception)
I agree with many people here that at
is mostly useless; however it might look better when working with pointers to (or lists of) containers:
std::vector<std::vector<int> >::iterator i;
for (i = v2.begin(); i != v2.end(); ++i)
{
int x1 = (*i)[3]; // UGLY
int x2 = i->at(3); // Looks OK
}
I think this code looks better when using at
.
Stroustrup recommends use of at
at all places except when you are sure that the index will be inside the valid range.
He recommends that for code like the below one can use the []
operator.
for (int i = 0; i < v.size(); ++i)
{
// use v[i] - we are sure it will be a valid index
}
In other cases, use at
精彩评论