Reasonable expectation: for(it = v.begin(); it != v.end(); it++)

S

Steven T. Hatton

Suppose I have the following:
vector<T> v(20);
for(vector<T>::it = v.begin(); it != v.end(); ++it){/*do something*/}

What can I reasonably expect of a compiler regarding the call to v.end()?
IOW, does it cost more to evaluate v.end() for each iteration than it does
to use

size_t sz = v.size();
for(size_t i = 0; i < sz; i++){/*do something*/}
?

I'm talking about reasonable expectations not what a specific compiler does.

I have to wonder what the advantage of the first form above is over the
second. I know there are functions in <algorithm> which require iterators,
but for purposes of traversing a sequence in a for() loop, it seems
generally more convenient to use the 'traditional' array-style with []
subscripting. That, however, is not what I see in a lot of C++ literature.
 
I

Ian

Steven said:
Suppose I have the following:
vector<T> v(20);
for(vector<T>::it = v.begin(); it != v.end(); ++it){/*do something*/}

What can I reasonably expect of a compiler regarding the call to v.end()?
IOW, does it cost more to evaluate v.end() for each iteration than it does
to use

size_t sz = v.size();
for(size_t i = 0; i < sz; i++){/*do something*/}
?

I'm talking about reasonable expectations not what a specific compiler does.

I have to wonder what the advantage of the first form above is over the
second. I know there are functions in <algorithm> which require iterators,
but for purposes of traversing a sequence in a for() loop, it seems
generally more convenient to use the 'traditional' array-style with []
subscripting. That, however, is not what I see in a lot of C++ literature.

For vector, it's six of one and half a dozen of the other. end() may
just be an inline method and an iterator may just be a pointer. Don't
forget [] will also be a member call.

Another reason for using iterators is you may end up changing container
type as your design evolves. [] is far from trivial on a map. So the
iterator form makes your code more general.

Ian
 
R

Richard Herring

Steven T. Hatton said:
Suppose I have the following:
vector<T> v(20);
for(vector<T>::it = v.begin(); it != v.end(); ++it){/*do something*/}

What can I reasonably expect of a compiler regarding the call to v.end()?
IOW, does it cost more to evaluate v.end() for each iteration than it does
to use

size_t sz = v.size();
for(size_t i = 0; i < sz; i++){/*do something*/}
?

I'm talking about reasonable expectations not what a specific compiler does.

If you're really, justifiably (i.e. a profiler told you so!) worried
about the cost of repeatedly calling v.end(), why not just do this:

for (whatever::iterator it=v.begin(), e=e.end(); it!=e; ++it) {...}

?
I have to wonder what the advantage of the first form above is over the
second. I know there are functions in <algorithm> which require iterators,
but for purposes of traversing a sequence in a for() loop, it seems
generally more convenient to use the 'traditional' array-style with []
subscripting.

Until someone decides to substitute a list for the vector.
 
S

Steven T. Hatton

Richard said:
If you're really, justifiably (i.e. a profiler told you so!) worried
about the cost of repeatedly calling v.end(), why not just do this:

for (whatever::iterator it=v.begin(), e=e.end(); it!=e; ++it) {...}

?

I haven't started messing with a profiler yet - it's not a bad suggestion -,
but in some of the things I do, number crunching is an issue.

Yes, that is an option. For the sake of readability, I would prefer:

whatever::iterator e=v.end();
for (whatever::iterator it=v.begin(); it!=e; ++it) {...}

That will work 99.9% of the time. I am very unlikely to try to grow the
vector in the for() loop, however...
 
T

tom_usenet

Suppose I have the following:
vector<T> v(20);
for(vector<T>::it = v.begin(); it != v.end(); ++it){/*do something*/}

What can I reasonably expect of a compiler regarding the call to v.end()?
IOW, does it cost more to evaluate v.end() for each iteration than it does
to use

size_t sz = v.size();
for(size_t i = 0; i < sz; i++){/*do something*/}
?

I'm talking about reasonable expectations not what a specific compiler does.

It certainly makes the job of the compiler. If your "do something" is
sufficiently complex, the call can't be optimized away (although it
will almost certainly be inlined). A common technique is to do:

for(vector<T>::it = v.begin(), end = v.end();
it != end;
++it){
/*do something*/
}
I have to wonder what the advantage of the first form above is over the
second. I know there are functions in <algorithm> which require iterators,
but for purposes of traversing a sequence in a for() loop, it seems
generally more convenient to use the 'traditional' array-style with []
subscripting. That, however, is not what I see in a lot of C++ literature.

The subscripting is only efficient for std::vector; in that specific
case, using subscripting is fine. For other containers you have to use
iterators (and for std::deque it may be more efficient).

Tom
 
G

Greg Schmidt

I haven't started messing with a profiler yet - it's not a bad suggestion -,
but in some of the things I do, number crunching is an issue.

In 11 years in the industry, I've only ever once needed to get into
profiling. Then again, I may be the exception to the rule, most of what I
write spends its time waiting for user input, and whether it takes 0.1 or
0.3 seconds to respond is largely irrelevant.
Yes, that is an option. For the sake of readability, I would prefer:

whatever::iterator e=v.end();
for (whatever::iterator it=v.begin(); it!=e; ++it) {...}

The drawback of this is that the scope of e is now much larger than if it
were declared in the for statement.
That will work 99.9% of the time. I am very unlikely to try to grow the
vector in the for() loop, however...

If you grow the vector inside the for loop, then you're asking for all
kinds of trouble. Not only will end() change, but your "it" iterator may
be invalidated by a reallocation. (Of course, you might be able to ensure
that enough space is reserved before entering the for loop.)
 
S

Steven T. Hatton

Greg said:
In 11 years in the industry, I've only ever once needed to get into
profiling. Then again, I may be the exception to the rule, most of what I
write spends its time waiting for user input, and whether it takes 0.1 or
0.3 seconds to respond is largely irrelevant.

Certainly most of what I am likely to write falls into that category as
well. It may be worth considering how much you are asking the processor to
do even if your own response time isn't an issue. In most cases it is more
reasonable to focus on writing understandable code than on writing highly
optimize code.
The drawback of this is that the scope of e is now much larger than if it
were declared in the for statement.

Agreed. I will say that I tend to keep the size of any given function fairly
small, so scope does not become a major issue. In the above case, I would
probably prefer to mess up the containing scope a bit, rather than write
hard to read code.

But all of this seems moot now that I have given it further thought.
v.end() is most likely going to be represented as a pointer with a fixed
memory location containing the value of the address immediately following
the last element of the vector's physical representation. The 'management'
members of the vector are unlikely to be relocated if the vector is
resized. That means v.end() will have a fixed address throughout its
lifetime. The value stored at that address will be changed if the vector
is resized. The comparison `it != v.end()' is between pointer values, so
using e=v.end() buys you nothing. `whatever::iterator e' is as indirect as
v.end().
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top