Should I prefer iterators over const_iterators?

2019-01-22 18:15发布

问题:

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?

回答1:

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...



回答2:

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 ;)



回答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.



回答4:

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?



回答5:

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.



回答6:

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.