Подтвердить что ты не робот

Является ли сравнение 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?

4b9b3361

Ответ 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.