Является ли сравнение 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
?
Ответы
Ответ 1
Удивительно, но С++ 98 и С++ 11 не сказали, что вы можете сравнить iterator
с const_iterator
. Это приводит к проблеме LWG 179 и LWG-проблеме 2263. Теперь в С++ 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
тип, ссылающийся на тот же элемент без изменения семантики.
Ответ 2
См. §23.2.1, Таблица 96:
X::iterator
[...]
любая категория итератора, соответствующая требованиям прямого итератора.
конвертируется в X::const_iterator
Итак, да, это четко определено.
Ответ 3
Таблица 96 в стандарте С++ 11 в разделе 23.2.1 определяет эксплуатационную семантику a.cend()
для любого типа контейнера X
(включая std::vector
) следующим образом:
const_cast<X const &>(a).end()
Таким образом, ответ "да", потому что это определение cend()
относится к тому же элементу/позиции в контейнере как end()
, а X::iterator
должно быть конвертировано в X::const_iterator
(требование также указано в той же таблице (& AST;).)
(Ответ также да для begin()
vs. cbegin()
по тем же причинам, что и в той же таблице.)
(& ast;) В комментариях к другим ответам было указано, что конвертируемость не обязательно означает, что операция сравнения i1==i2
всегда будет работать, например. если operator==()
является функцией-членом типа итератора, неявное преобразование будет приниматься только для аргумента правой стороны, а не для левого и правого. 24.2.5/6 (о форвард-итераторах a
и b
):
Если a
и b
являются разыменованными, то a == b
тогда и только тогда, когда *a
и *b
привязаны к одному и тому же объекту
Несмотря на то, что итераторы end()
и cend()
не являются разыменовываемыми, приведенное выше утверждение подразумевает, что operator==()
должно быть определено таким образом, чтобы сравнение было возможным, даже если a
является константным итератором и b
не является, и наоборот, потому что 24.2.5 относится к форвард-итераторам вообще, включая как const, так и не-const-версии - это ясно, например от 24.2.5/1. Вот почему я убежден, что формулировка из Таблицы 96, которая касается конвертируемости, также подразумевает сопоставимость. Но, как описано в cpplearner @позже, это было сделано явным образом только в С++ 14.