Consider the following code:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> vec{1,2,3,5};
for(auto it=vec.cbegin();it!=vec.cend();++it)
{
std::cout << *it;
// A typo: end instead of cend
if(next(it)!=vec.end()) std::cout << ",";
}
std::cout << "\n";
}
Here I've introduced a typo: in the comparison I called vec.end()
instead of vec.cend()
. This appears to work as intended with gcc 5.2. But is it actually well-defined according to the Standard? Can iterator
and const_iterator
be safely compared?
Surprisingly, C++98 and C++11 didn't say that you can compare a iterator
with a const_iterator
. This leads to LWG issue 179 and LWG issue 2263. Now in C++14, this is explicitly permitted by § 23.2.1[container.requirements.general]p7
In the expressions
i == j
i != j
i < j
i <= j
i >= j
i > j
i - j
where i
and j
denote objects of a container's iterator
type, either or
both may be replaced by an object of the container's const_iterator
type referring to the same element with no change in semantics.
See §23.2.1, Table 96:
X::iterator
[...]
any iterator category that meets the forward iterator requirements.
convertible to X::const_iterator
So, yes, it is well-defined.
Table 96 in the C++11 Standard, in section 23.2.1, defines the operational semantics of a.cend()
for any container type X
(including std::vector
) as follows:
const_cast<X const &>(a).end()
So the answer is yes because by this definition cend()
refers to the same element/position in the container as end()
, and X::iterator
must be convertible to X::const_iterator
(a requirement also specified in the same table(*)).
(The answer is also yes for begin()
vs. cbegin()
for the same reasons, as defined in the same table.)
(*) It has been pointed out in comments to other answers that convertibility does not necessarily imply that the comparison operation i1==i2
will always work, e.g. if operator==()
is a member function of the iterator type, the implicit conversion will only be accepted for the righthand-side argument, not the lefthand-side one. 24.2.5/6 states (about forward-iterators a
and b
):
If a
and b
are both dereferenceable, then a == b
if and only if *a
and *b
are bound to the same object
Even though the iterators end()
and cend()
are not dereferenceable, the statement above implies that operator==()
must be defined in such a way that the comparison is possible even if a
is a const-iterator and b
is not, and vice versa, because 24.2.5 is about forward-iterators in general, including both const- and non-const-versions -- this is clear e.g. from 24.2.5/1. This is why I am convinced that the wording from Table 96, which refers to convertibility, also implies comparability. But as described in cpplearner@'s later answer, this has been made explicitly clear only in C++14.