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

Вызов стирания с помощью итератора vs const_iterator

Почему вызов функции erase члена контейнера с const_iterator не выполняется?

Он работает с не const iterator.

4b9b3361

Ответ 1

Это не компилируется, потому что container::iterator и container::const_iterator являются двумя разными типами, и единственная (однопараметрическая) версия стирания: iterator erase(iterator);

Не принимать значение const_iterator можно рассматривать как дефект в стандарте языка: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2350.pdf

Нет особых причин для этого ограничения. Итератор используется только для для указания позиции в (модифицируемом) контейнере, и ни в случае insert, либо erase не является "клиентом" измененного итератора (в случае erase он просто концептуально выходит из существования, что является нормальной задачей для объектов const).

Текущий стандарт указывает на путаницу между "константой итератора и константой контейнера" (как и другие ответы здесь), и кажется, что const_iterator может стать приемлемым для erase в С++ 0x.


В качестве обходного пути вы можете достоверно получить iterator из const_iterator, потому что контейнер должен быть изменен в первую очередь.

Функция ниже компилируется только для итераторов произвольного доступа, так как это может быть слишком медленным, чтобы сделать это с другими типами итераторов.

#include <vector>

template <class Container>
typename Container::iterator to_mutable_iterator(Container& c, typename Container::const_iterator it)
{
    return c.begin() + (it - c.begin());
}

int main()
{
    int arr[] = {1, 5, 2, 5, 3, 4, 5, 1};
    std::vector<int> vec(arr, arr + sizeof(arr) / sizeof(*arr));
    for (std::vector<int>::const_iterator it = vec.begin(); it != vec.end(); ) {
        //if (*it = 5) {  //const_iterator prevents this error
        if (*it == 5) {
            it = vec.erase(to_mutable_iterator(vec, it));
        }
        else {
            ++it;
        }
    }
}

Однако лучше переструктурировать код, чтобы вам в первую очередь не нужен const_iterator. В этом случае было бы лучше использовать алгоритм std::remove. Если вам нужно выполнить больше не мутирующих действий перед стиранием, вы можете извлечь их в отдельный метод и т.д.

Ответ 2

Я просто хочу подчеркнуть общую правильность ответов/комментариев, опубликованных UncleBens, David Rodriguez и Ise Westeria.

Независимо от поведения текущих (или предыдущих) компиляторов pre-С++ 11, const-константа const_iterator (should) немедленно останавливается при том, что она семантически равна const T * (или T * const) - обратите внимание, что Сам Т может быть собственным типом const! - поэтому он эффективно предотвращает изменение кода ссылочного объекта в контейнере.

Однако, поскольку вполне корректно "удалять" указатель const в С++ (попробуйте, он работает!), он должен быть (и поведение исправлено на С++ 11) законно, а также "стереть" константный итератор из контейнера, если сам контейнер не const.

Кажется, что Visual Studio 2010 уже ведет себя правильно, "стирая" прием const_iterator, что, конечно, вызвало у меня некоторые головные боли, чтобы отследить какую-то другую ошибку, которая привела меня к этому сообщению, что в конечном итоге прояснило правильное поведение "стереть const_iterator" const.

Ответ 3

Тип const_iterator не может использоваться для изменения значения элемента или контейнера.

Ответ 4

Что касается константы, вы можете думать о std::container<T>::const_iterator как const T*.

Ответ 5

Это цель const_iterator. Вы используете их, когда элементы, доступные через него, не должны быть изменены.