开发者

++it or it++ when iterating over a map?

开发者 https://www.devze.com 2023-03-25 19:08 出处:网络
Examples showing how to iterate over a std::map are often like th开发者_Go百科at: MapType::const_iterator end = data.end();

Examples showing how to iterate over a std::map are often like th开发者_Go百科at:

MapType::const_iterator end = data.end(); 
for (MapType::const_iterator it = data.begin(); it != end; ++it)

i.e. it uses ++it instead of it++. Is there any reason why? Could there be any problem if I use it++ instead?


it++ returns a copy of the previous iterator. Since this iterator is not used, this is wasteful. ++it returns a reference to the incremented iterator, avoiding the copy.

Please see Question 13.15 for a fuller explanation.


Putting it to the test, I made three source files:

#include <map>

struct Foo { int a; double b; char c; };

typedef std::map<int, Foo> FMap;

### File 1 only ###

void Set(FMap & m, const Foo & f)
{
  for (FMap::iterator it = m.begin(), end = m.end(); it != end; ++it)
    it->second = f;
}

### File 2 only ###

void Set(FMap & m, const Foo & f)
{
  for (FMap::iterator it = m.begin(); it != m.end(); ++it)
    it->second = f;
}

### File 3 only ###

void Set(FMap & m, const Foo & f)
{
  for (FMap::iterator it = m.begin(); it != m.end(); it++)
    it->second = f;
}

### end ###

After compiling with g++ -S -O3, GCC 4.6.1, I find that version 2 and 3 produce identical assembly, and version 1 differs only in one instruction, cmpl %eax, %esi vs cmpl %esi, %eax.

So, take your pick and use whatever suits your style. Prefix increment ++it is probably best because it expresses your requirements most accurately, but don't get hung up about it.


There’s a slight performance advantage in using pre-increment operators versus post-increment operators. In setting up loops that use iterators, you should opt to use pre-increments:

for (list<string>::const_iterator it = tokens.begin();
    it != tokens.end();
    ++it) { // Don't use it++
    ...
}

The reason comes to light when you think about how both operators would typically be implemented.The pre-increment is quite straightforward. However, in order for post-increment to work, you need to first make a copy of the object, do the actual increment on the original object and then return the copy:

class MyInteger {
private:
    int m_nValue;

public:
    MyInteger(int i) {
        m_nValue = i;
    }

    // Pre-increment
    const MyInteger &operator++() {
        ++m_nValue;
        return *this;
    }

    // Post-increment
    MyInteger operator++(int) {
        MyInteger clone = *this; // Copy operation 1
        ++m_nValue;
        return clone; // Copy operation 2
    }
}

As you can see, the post-increment implementation involves two extra copy operations. This can be quite expensive if the object in question is bulky. Having said that, some compilers may be smart enough to get away with a single copy operation, through optimization. The point is that a post-increment will typically involve more work than a pre-increment and therefore it’s wise to get used to putting your “++”s before your iterators rather than after.

(1) Credit to linked website.


At logical point of view - it's the same and it doesn't matter here.

Why the prefix one is used - because it's faster - it changes the iterator and returns its value, while the postfix creates temp object, increments the current iterator and then returns the temp object (copy of the same iterator, before the incrementing ). As no one watches this temp object here (the return value), it's the same (logically).

There's a pretty big chance, that the compiler will optimize this.


In Addition - actually, this is supposed to be like this for any types at all. But it's just supposed to be. As anyone can overload operator++ - the postfix and prefix, they can have side effects and different behavior.

Well, this is a horrible thing to do, but still possible.


It won't cause any problems, but using ++it is more correct. With small types it doesn't really matter to use ++i or i++, but for "big" classes:

operator++(type x,int){
    type tmp=x; //need copy
    ++x;
    return tmp;
}

The compiler may optimize out some of them, but it's hard to be sure.


As other answers have said, prefer ++it unless it won't work in context. For iterating over containers of small types it really makes little difference (or no difference if the compiler optimizes it away), but for containers of large types it can make a difference due to saving the cost of making a copy.

True, you might know in your specific context that the type is small enough so you don't worry about it. But later, someone else on your team might change the contents of the container to where it would matter. Plus, I think it is better to get yourself into a good habit, and only post-increment when you know you must.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号