Ответы:
const_iterator
s не позволяют вам изменять значения, на которые они указывают, обычные iterator
s делают.
Как и во всех вещах в C ++, всегда предпочитайте const
, если только нет веской причины использовать обычные итераторы (т.е. вы хотите использовать тот факт, что они не const
изменяют указанное значение).
Они должны быть в значительной степени очевидны. Если итератор указывает на элемент типа T, тогда const_iterator указывает на элемент типа 'const T'.
Это в основном эквивалентно типам указателей:
T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator
Константный итератор всегда указывает на тот же элемент, поэтому итератора сам по себе является Const. Но элемент, на который он указывает, не обязательно должен быть константным, поэтому элемент, на который он указывает, можно изменить. Const_iterator - это итератор, который указывает на элемент const, поэтому, хотя сам итератор может быть обновлен (например, увеличен или уменьшен), элемент, на который он указывает, не может быть изменен.
const iterater
и const_iterator
.
К сожалению, многие методы для контейнеров STL принимают итераторы вместо const_iterators в качестве параметров. Поэтому, если у вас есть const_iterator , вы не можете сказать «вставить элемент перед элементом, на который указывает этот итератор» (говоря, что, на мой взгляд, такая вещь не является концептуальным нарушением const). В любом случае, если вы хотите сделать это, вам нужно преобразовать его в неконстантный итератор, используя std :: advance () или boost :: next () . Например. boost :: next (container.begin (), std :: distance (container.begin (), the_const_iterator_we_want_to_unconst)) . Если контейнер представляет собой std :: list , то время выполнения этого вызова будет O (n) .
Поэтому универсальное правило добавлять const везде, где это «логично», менее универсально, когда дело касается контейнеров STL.
Однако контейнеры boost принимают const_iterators (например, boost :: unordered_map :: erase ()). Поэтому, когда вы используете буст-контейнеры, вы можете быть «агрессивными». Кстати, кто-нибудь знает, будут ли исправлены контейнеры STL?
vector
и deque
, вставка одного элемента делает недействительными все существующие итераторы, что не очень const
. Но я понимаю вашу точку зрения. Такие операции защищены контейнером const
, а не итераторами. И мне непонятно, почему в стандартном интерфейсе контейнера нет функции преобразования итератора const в nonconst.
int const * foo;
int * const foo;
и int const * const foo;
все три являются действительными и полезными, каждый по-своему. std::vector<int> const bar
должен быть таким же, как второй, но, к сожалению, к нему часто относятся как к третьему. Основной причиной проблемы является то, что мы не можем сказать, std::vector<int const> bar;
когда означает, что нет никакого способа получить тот же эффект, что и int const *foo;
в векторе.
Минимальные выполнимые примеры
Неконстантные итераторы позволяют вам изменять то, на что они указывают:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);
Const итераторы не делают:
const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
Как показано выше, v.begin()
он const
перегружен и возвращает либо значение, iterator
либо const_iterator
зависящее от константы переменной контейнера:
Распространенный случай, когда const_iterator
всплывающее окно - это когда this
используется внутри const
метода:
class C {
public:
std::vector<int> v;
void f() const {
std::vector<int>::const_iterator it = this->v.begin();
}
void g(std::vector<int>::const_iterator& it) {}
};
const
делает this
const, что делает this->v
const.
Обычно вы можете об этом забыть auto
, но если вы начнете передавать эти итераторы, вам придется подумать о них для сигнатур методов.
Во многом как const и не-const, вы можете легко конвертировать из не-const в const, но не наоборот:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
// non-const to const.
std::vector<int>::const_iterator cit = it;
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
// Compile time error: no conversion from const to no-const.
//it = ci1;
Какой из них использовать: аналогично const int
vs int
: предпочитайте константные итераторы всякий раз, когда вы можете их использовать (когда вам не нужно изменять контейнер с ними), чтобы лучше документировать свое намерение читать без изменений.
(как уже говорили другие) const_iterator не позволяет вам изменять элементы, на которые он указывает, это полезно внутри методов класса const. Это также позволяет вам выразить свое намерение.
Хорошо, позвольте мне сначала объяснить это на очень простом примере, не используя константный итератор. Рассмотрим коллекцию случайных целых чисел "randomData"
for(vector<int>::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0;
for(vector<int>::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout << *i;
Как можно видеть для записи / редактирования данных внутри коллекции, используется обычный итератор, но для чтения используется постоянный итератор. Если вы попытаетесь использовать константный итератор в первом цикле for, вы получите ошибку. В качестве правила большого пальца используйте постоянный итератор для чтения данных внутри коллекции.