Вызов стереть с помощью итератора против const_iterator - PullRequest
21 голосов
/ 03 февраля 2011

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

Работает с неконстантным iterator.

Ответы [ 5 ]

26 голосов
/ 03 февраля 2011

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

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

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

Текущий стандарт указывает на путаницу между «константностью итератора и константностью контейнера» (как и другие ответы здесь), и кажется,const_iterator может стать приемлемым для erase в C ++ 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.Если перед удалением вам нужно выполнить больше работы без мутаций, вы можете извлечь ее в отдельный метод и т. Д.

5 голосов
/ 01 октября 2012

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

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

Однако, поскольку вполне законно «удалять» константный указатель в C ++ (попробуйте, он работает!), это должно быть (иэто поведение было исправлено в C ++ 11) также допустимо «стереть» константный итератор из контейнера, при условии, что сам контейнер не является константой.

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

3 голосов
/ 03 февраля 2011

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

2 голосов
/ 03 февраля 2011

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

0 голосов
/ 03 февраля 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...