views:

1020

answers:

5

Someone here recently brought up the article from Scott Meyers that says:

  • Prefer iterators over const_iterators (pdf link).

Someone else was commenting that the article is probably outdated. I'm wondering what your opinions are?

Here is mine: One of the main points of the article is that you cannot erase or insert on a const_iterator, but I think it's funny to use that as an argument against const_iterators. I thought the whole point of const_iterators it that you do not modify the range at all, neither the elements themselves by substituting their values nor the range by inserting or erasing. Or am I missing something?

+7  A: 

I totally agree with you. I think the answer is simple: Use const_iterators where const values are the right thing to use, and vice versa. Seems to me that those who are against const_iterators must be against const in general...

Gal Goldman
+4  A: 

I don't think this particular statement of Meyer's needs to be taken with special concern. When you want a non-modifying operation, it is best to use a const_iterator. Otherwise, use an ordinary iterator. However, do note the one important thing: Never mix iterators i.e. const ones with non-const ones. As long as you are aware of the latter, you should be fine.

dirkgently
+4  A: 

Here's a slightly different way to look at it. Const_iterator almost never makes sense when you are passing it as a pointer into a specific collection and you are passing the collection as well. Mr. Meyer was specifically stating that const_iterator cannot be used with most member functions of a collection instance. In that case, you will need a plain-old iterator. However, if you don't have a handle to the collection, the only difference between the two is that you can modify what is pointed to by an iterator and you can't modify the object referenced by a const_iterator.

So... you want to use iterator whenever you are passing a collection and position into the collection to an algorithm. Basically, signatures like:

void some_operation(std::vector<int>& vec, std::vector::const_iterator pos);

don't make a whole lot of sense. The implicit statement is that some_operation is free to modify the underlying collection but is not allowed to modify what pos references. That doesn't make much sense. If you really want this, then pos should be an offset instead of an iterator.

On the flip side, most of the algorithms in the STL are based on ranges specified by a pair of iterators. The collection itself is never passed so the difference between iterator and const_iterator is whether the value in the collection can be modified through the iterator or not. Without a reference to the collection, the separation is pretty clear.

Hopefully that made things as clear as mud ;)

D.Shawley
it would also apply if you had access to the container like in a method where the container is a class attribute and the method get's a const_iterator as an argument.
lothar
and in that case Meyer's is right that even if the method is non const (and could modify the collection by e.g. calling erase. You could not implement that API because you can not get an iterator (that you need for erase) from the const_iterator that comprises the method API.
lothar
If you have a non-const version of the collection, you can (almost) always get a modifiable iterator using "iter = coll.begin(); std::advance(iter, std::distance(coll.begin(), constIter));" not that you should do such things even if the language lets you.
D.Shawley
@D.Shawley: +1, that's an insightful way to look at it. You're right, Meyers seems to miss the whole point of const correctness in this article. That being, that if you declare a function to take a const_iterator parameter (and no parameter that is a non-const ref to the container), the caller **knows** that you won't modify the container.
j_random_hacker
Yup... it is even more explicit than that. By design, STL does not make it possible to get a handle to the container through an iterator. So the only way that the container can be modified is if the user provides access to it explicitly. Of course this breaks a little if the object that you are providing the iterator to already owns the container as mentioned by others in this thread.
D.Shawley
+3  A: 

By my reading of that link, Meyers appears to be fundamentally saying that interator's are better than const_interator's because you cannot make changes via a const_iterator.

But if that is what he is saying then Meyers is in fact wrong. This is precisely why const_iterator's are better than iterator's when that is what you want to express.

John Dibling
+1  A: 

I generally prefer constness, but recently came across a conundrum with const_iterators that has confused my "always use const were possible" philosophy:

MyList::const_iterator find( const MyList & list, int identifier )
{
    // do some stuff to find identifier
    return retConstItor;
}

Since passing in a const list reference required that I only use const iterators, now if I use the find, I cannot do anything with the result but look at it even though all I wanted to do was express that find would not change the list being passed in.

I wonder perhaps, then, if Scott Mayers advice has to do with issues like this where it becomes impossible to escape const-ness. From what I understand, you cannot (reliably) un-const const_iterators with a simple cast because of some internal details. This also (perhaps in conjunction) be the issue.

this is probably relevant: http://stackoverflow.com/questions/765148/how-to-remove-constness-of-constiterator

Catskul
"I cannot do anything with the result" And that's the point of const. If you change the data, you're changing the list.
GMan
Well semantically the const in this case is mean only to say "this *function* will not modify the list". The problem isn't const per-say, but the cont_iterator concept. If you have access to a non-const version of the list, you should be able to use the (const)iterator to modify the item pointed to on that list. And technically you can, but it's not guaranteed to be a constant time operation which it should be.As far as I'm concerned, all (non-const) stl containers should have a manner for converting cont_iterators to non const iterators (in constant time)
Catskul
If the function is returning a mutable iterator, it's providing a method to change the list.
GMan
I'm not saying it should be possible to pass back a non-const interator. But that if you have a non-const version of the list, it should be possible to convert the const_iterator to non-const iterator.
Catskul
If you have a non-const version, pass it into the function. Either you want the list modified or you don't.
GMan
*Of course* you can simply use/create a version that doesn't specify const. However one of the major reasons to use "const" is to guarantee that the function will not modify the contents. The point is that use in that way doesnt work well here.
Catskul