27

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?

Community
  • 1
  • 1
Frank
  • 64,140
  • 93
  • 237
  • 324
  • +1 -- I had very similar thoughts when reading that discussion. – Reunanen Apr 19 '09 at 11:35
  • 3
    I started writing an answer about how you're missing the point but then I realized Meyer is indeed not making much sense. –  Apr 19 '09 at 12:21
  • 1
    For seaching purposes, I suggest that you correct the author's name to Scott Meyers. – Daniel Daranas Apr 23 '09 at 09:37
  • 1
    The reason Mr. Meyers preferred `iterator`s was that methods like `std::vector::insert`/`std::vector::erase` all took `iterator`s, so having anything else (such as a `const_iterator`) would do you no good. But since C++11, those methods are taking `const_iterator`s ([`std::vector::insert`](http://en.cppreference.com/w/cpp/container/vector/insert), [`std::vector::erase`](http://en.cppreference.com/w/cpp/container/vector/erase)), so there is no reason to prefer `iterator` over `const_iterator` now. Use the `const` version whenever you can. – Zizheng Tai Jun 24 '16 at 21:25

6 Answers6

22

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
  • 8,641
  • 11
  • 45
  • 45
9

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
  • 58,213
  • 10
  • 98
  • 113
  • 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 Apr 20 '09 at 03:17
  • 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 Apr 20 '09 at 03:20
  • 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 Apr 20 '09 at 03:37
  • @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 Apr 20 '09 at 13:23
  • 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 Apr 20 '09 at 14:55
3

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: How to remove constness of const_iterator?

Community
  • 1
  • 1
Catskul
  • 17,916
  • 15
  • 84
  • 113
  • "I cannot do anything with the result" And that's the point of const. If you change the data, you're changing the list. – GManNickG Oct 12 '09 at 22:08
  • 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 Oct 12 '09 at 22:54
  • If the function is returning a mutable iterator, it's providing a method to change the list. – GManNickG Oct 12 '09 at 23:16
  • 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 Oct 12 '09 at 23:32
  • If you have a non-const version, pass it into the function. Either you want the list modified or you don't. – GManNickG Oct 13 '09 at 00:41
  • *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 Oct 13 '09 at 00:50
  • The ugly solution is to provide two versions of the function, one that takes a const reference and returns a `const_iterator` and another that takes a non-const reference and returns an `iterator`. Any function that returns a non-const pointer, reference, or iterator should take a non-const parameter, even if it doesn't change the object itself. – Mark Ransom Sep 27 '12 at 17:04
  • 1
    I should add that this problem has been solved since C++11. `insert`/`erase` etc. now all takes `const_iterator`s. – Zizheng Tai Jun 24 '16 at 21:21
3

C++98

I think one needs to take into account that Meyers statement refers to c++98. Hard to tell today, but if I remember right

  • it simply was not easy to get a const_iterator for a non const container at all
  • if you got a const_iterator you could have hardly made any use of it since most (all?) position arguments for container member functions were expected to be iterators and not const_iterators

e.g.

std::vector<int> container;

would have required

static_cast<std::vector<int>::const_iterator>(container.begin())

to get a const_iterator, which would have considerably inflated a simple .find and even if you had your result then after

std::vector<int>::const_iterator i = std::find(static_cast<std::vector<int>::const_iterator>(container.begin()), static_cast<std::vector<int>::const_iterator>(container.end()),42);

there would have been no way to use your std::vector::const_iterator for insertion into the vector or any other member function that expected iterators for a position. And there was no way to get a iterator from a const iterator. No way of casting existed (exists?) for that.

Because const iterator does not mean that the container could not be changed, but only that the element pointed to could not been changed (const iterator being the equivalent of pointer to const) that was really a big pile of crap to deal with in such cases.

Today the opposite is true.

const iterators are easy to get using cbegin etc. even for non const containers and all (?) member functions that take positions have const iterators as their arguments so there is no need for any conversion.

std::vector<int> container;                
auto i = std::find(container.cbegin(), container.cend(), 42); 
container.insert(i, 43); 

So what once was

Prefer iterators over const_iterators

today really really should be

Prefer const_iterators over iterators

since the first one is simply an artifact of historical implementation deficits.

DrSvanHay
  • 1,170
  • 6
  • 16
  • The real problem with const iterators in C++98 was that container own methods did not accept them. You want to `erase` element, you got `const_iterator` for? Sucks to be you, `erase` accepted only `iterator` back then. Insert after that element? Find a way to convert to `iterator`. So, constant iterators were largely useless. – Revolver_Ocelot Aug 22 '17 at 19:33
3

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
  • 108,024
  • 16
  • 131
  • 187
2

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
  • 99,718
  • 31
  • 186
  • 324
  • The problem is if you have a const_iterator along with a non-const container. You should be able to use the const_iterator to indicate to the container where you want it to change, since the container is not itself const - but you can't. Look at Catskul's example of find - it should take a const container and return a const_iterator - but then you can't erase what you found from the container even if the container you have is not const. – Bjarke H. Roune Sep 02 '11 at 23:38