orlp
orlp

Reputation: 117691

Semantic iterator declaration?

I'm sorry for the very vague title, I really didn't know how to title this question.

Let's say I have this:

std::list<std::string> msgs;

for (std::list<std::string>::iterator it = msgs.begin(); it < msgs.end(); it++) {
    // ...
}

For me, this is hard to to read. The std::list<std::string>::iterator almost seems like a magic number, especially if the declaration of msgs is far away, like in an header file. IMO it would be easier to read and much more semantic if it were something like this:

std::list<std::string> msgs;

for (msgs.iterator it = msgs.begin(); it < msgs.end(); it++) {
    // ...
}

Now, this is obviously illegal C++. But my question is, is there a way of implementing something that supports writing iterator declarations like this?

Upvotes: 11

Views: 3227

Answers (5)

stijn
stijn

Reputation: 35901

If you provide a typedef it will make life a lot easier:

typedef std::list<std::string>::iterator ListIterator;
for( ListIterator it = msgs.begin(); it != msgs.end(); ++it ){}

Other than that, c++0x does it for you by providing the auto keyword:

for( auto it = msgs.begin(); it != msgs.end(); ++it ){}

edit like 5 years after date, but I updated < tp != and using preincrement because that's actually what I've been doing for ages, and makes more sense to me, to the point it makes me wonder how I ever wrote this answer not using that

Upvotes: 16

MSalters
MSalters

Reputation: 179819

The canonical solution is

std::for_each(msgs.begin(), msgs.end(), ... );

Before C++0x, writing the ... bit used to be a bit hard (if it was anything complex), but we now have lambda's.

Upvotes: 0

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385154

There are two practical, canonical approaches:

typedef std::list<int> list_t;
list_t l;

// later:
for (list_t::iterator it = l.begin(), end = l.end(); it != end; ++it) {}

And the C++0x-only auto:

std::list<int> l;

// later:
for (auto it = l.begin(), end = l.end(); it != end; ++it) {}

In addition, C++0x allows:

std::list<int> l;

// later:
for (decltype(l)::iterator it = l.begin(), end = l.end(); it != end; ++it)
   std::cout << *it << ", ";

I think that this is the closest match to what you were specifically asking (even if it's not necessarily the best solution).

The downside is that the ability to apply the scope operator (::) to decltype link was only voted into the working paper relatively recently, and I'm not aware of any compilers that support it yet (GCC 4.5.1 does not).

Upvotes: 4

Bj&#246;rn Pollex
Bj&#246;rn Pollex

Reputation: 76788

All containers have these typedefs to enable the writing of very generic code. Consider the following function-template:

template<class T>
void foo(const T& container) {
    for(T::const_iterator it = T.begin(); it != T.end(); it++) {
        // do stuff
    }
}

This enables you to write code that can work with any object that has defines a type const_iterator and a method begin() and end().

In C++0x the problem is solved with the introduction of the auto keyword:

for(auto it = container.begin(); it != container.end(); it++) {
    // do stuff
}

Upvotes: 0

Blazes
Blazes

Reputation: 4779

You can use typedef to clean that up:

typedef std::list<std::string>::iterator string_iterator;

std::list<std::string> msgs;

for (string_iterator it = msgs.begin(); it != msgs.end(); it++) {
    // ...
}

Upvotes: 1

Related Questions