Является ли сравнение const_iterator с итератором хорошо определенным?
Рассмотрим следующий код:
#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";
}
Здесь я ввел опечатку: в сравнении я назвал vec.end()
вместо vec.cend()
, Похоже, что это работает как задумано с gcc 5.2. Но действительно ли это четко определено в соответствии со Стандартом? Можно iterator
а также const_iterator
быть в безопасности в сравнении?
3 ответа
Удивительно, но C++98 и C++11 не сказали, что вы можете сравнить iterator
с const_iterator
, Это приводит к выпуску 179 LWG и выпуску 2263 LWG. Теперь в C++14 это явно разрешено § 23.2.1[container.requirements.general]p7
В выражениях
i == j i != j i < j i <= j i >= j i > j i - j
где
i
а такжеj
обозначить объекты контейнераiterator
Тип, один или оба могут быть заменены объектом контейнераconst_iterator
тип ссылается на один и тот же элемент без изменения семантики.
Таблица 96 в стандарте C++11 в разделе 23.2.1 определяет операционную семантику a.cend()
для любого типа контейнера X
(в том числе std::vector
) следующее:
const_cast<X const &>(a).end()
Так что ответ да, потому что по этому определению cend()
относится к тому же элементу / положению в контейнере, что и end()
, а также X::iterator
должен быть конвертируемым в X::const_iterator
(требование также указано в той же таблице(*)).
(Ответ также да для begin()
против cbegin()
по тем же причинам, что определены в той же таблице.)
(*) В комментариях к другим ответам было указано, что конвертируемость не обязательно означает, что операция сравнения i1==i2
всегда будет работать, например, если operator==()
является функцией-членом типа итератора, неявное преобразование будет приниматься только для правого аргумента, а не для левого. 24.2.5/6 состояний (о форвард-итераторах a
а также b
):
Если
a
а такжеb
оба разыменовываются, тоa == b
если и только если*a
а также*b
привязаны к одному и тому же объекту
Хотя итераторы end()
а также cend()
не разыменование, приведенное выше утверждение подразумевает, что operator==()
должны быть определены таким образом, чтобы сравнение было возможно, даже если a
является константным итератором и b
это не так, и наоборот, потому что 24.2.5 касается прямых итераторов в целом, включая как const-, так и non-const-версии - это ясно, например, из 24.2.5/1. Вот почему я убежден, что формулировка из таблицы 96, которая относится к конвертируемости, также подразумевает сопоставимость. Но, как описано в последующем ответе cpplearner@, это стало ясно ясно только в C++14.
См. §23.2.1, Таблица 96:
X::iterator
[...]
любая категория итераторов, отвечающая требованиям прямого итератора.
конвертируемый в
X::const_iterator
Так что да, это четко определено.