Ну, имя const_iterator звучит так, как будто итератор является const, в то время как этот итератор указывает на фактическое значение const.
talekeDskobeDa
Ответы:
124
const_iterators не позволяют вам изменять значения, на которые они указывают, обычные iterators делают.
Как и во всех вещах в C ++, всегда предпочитайте const, если только нет веской причины использовать обычные итераторы (т.е. вы хотите использовать тот факт, что они не constизменяют указанное значение).
В идеальном мире это было бы так. Но с C ++ const хорош только тем, кто написал код :(
JaredPar
изменчивый существует по очень хорошей причине. Он редко используется, и, глядя на Поиск кода Google, кажется, что существует достаточный процент допустимых применений. Ключевое слово - очень мощный инструмент оптимизации, и его удаление не улучшит правильность констант ( coughpointerscoughandcoughreferencescough )
coppro
2
Больше похоже на мощный хак. Из всех случаев, когда я когда-либо видел использование ключевого слова 'mutable', все, кроме одного, были точным индикатором того, что код был плохо написан, и изменчивость нужна была как хак для обхода дефектов.
Джон Диблинг
7
Он имеет законное применение, например, кэширование результатов длинных вычислений в классе const. С другой стороны, это почти единственный раз, когда я использовал изменяемый за почти двадцать лет разработки C ++.
Глава Geek
Типичным примером использования const_iterator может служить случай, когда итератор является rvalue
talekeDskobeDa
40
Они должны быть в значительной степени очевидны. Если итератор указывает на элемент типа 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>::iteratorconst T*// A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator
Константный итератор всегда указывает на тот же элемент, поэтому итератора сам по себе является Const. Но элемент, на который он указывает, не обязательно должен быть константным, поэтому элемент, на который он указывает, можно изменить. Const_iterator - это итератор, который указывает на элемент const, поэтому, хотя сам итератор может быть обновлен (например, увеличен или уменьшен), элемент, на который он указывает, не может быть изменен.
«Константный итератор всегда указывает на один и тот же элемент», это неверно.
Джон Диблинг
2
Как так? Обратите внимание на отсутствующее подчеркивание. Я сравниваю переменную типа const std :: vector <T> :: iterator с std :: vector <T> :: const_iterator. В первом случае сам итератор является константным, поэтому его нельзя изменить, но элемент, на который он ссылается, может быть изменен свободно.
Джалф
4
Ах я вижу. Да, я пропустил недостающее подчеркивание.
Джон Диблинг
3
@JohnDibling Upvoted за объяснение тонкости между const iteraterи const_iterator.
legends2k
8
К сожалению, многие методы для контейнеров 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.
Potatoswatter
Вы правы, Potatoswatter, я категорически, это вопрос мнения о контейнерах произвольного доступа и container.begin () + (в любом случае the_const_iterator_we_want_to_unconst - container.begin ()) равен O (1) . Мне также интересно, почему нет функции преобразования для контейнеров без произвольного доступа, но, может быть, есть веская причина? Знаете ли вы, есть ли какая-то причина, по которой функции для контейнеров неслучайного доступа не принимают const_iterators ?
Магнус Андермо
«По моему мнению, такое высказывание не является концептуальным нарушением const», - это очень интересный комментарий, у меня есть следующие мысли по теме. С простыми указателями можно сказать, int const * foo;int * const foo;и int const * const foo;все три являются действительными и полезными, каждый по-своему. std::vector<int> const barдолжен быть таким же, как второй, но, к сожалению, к нему часто относятся как к третьему. Основной причиной проблемы является то, что мы не можем сказать, std::vector<int const> bar;когда означает, что нет никакого способа получить тот же эффект, что и int const *foo;в векторе.
dgnuff
5
Используйте 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делает thisconst, что делает this->vconst.
Обычно вы можете об этом забыть 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 intvs 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, вы получите ошибку. В качестве правила большого пальца используйте постоянный итератор для чтения данных внутри коллекции.
Ответы:
const_iterator
s не позволяют вам изменять значения, на которые они указывают, обычныеiterator
s делают.Как и во всех вещах в C ++, всегда предпочитайте
const
, если только нет веской причины использовать обычные итераторы (т.е. вы хотите использовать тот факт, что они неconst
изменяют указанное значение).источник
Они должны быть в значительной степени очевидны. Если итератор указывает на элемент типа T, тогда const_iterator указывает на элемент типа 'const T'.
Это в основном эквивалентно типам указателей:
Константный итератор всегда указывает на тот же элемент, поэтому итератора сам по себе является 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;
в векторе.Используйте const_iterator, когда можете, используйте итератор, когда у вас нет другого выбора.
источник
Минимальные выполнимые примеры
Неконстантные итераторы позволяют вам изменять то, на что они указывают:
Const итераторы не делают:
Как показано выше,
v.begin()
онconst
перегружен и возвращает либо значение,iterator
либоconst_iterator
зависящее от константы переменной контейнера:Распространенный случай, когда
const_iterator
всплывающее окно - это когдаthis
используется внутриconst
метода:const
делаетthis
const, что делаетthis->v
const.Обычно вы можете об этом забыть
auto
, но если вы начнете передавать эти итераторы, вам придется подумать о них для сигнатур методов.Во многом как const и не-const, вы можете легко конвертировать из не-const в const, но не наоборот:
Какой из них использовать: аналогично
const int
vsint
: предпочитайте константные итераторы всякий раз, когда вы можете их использовать (когда вам не нужно изменять контейнер с ними), чтобы лучше документировать свое намерение читать без изменений.источник
(как уже говорили другие) const_iterator не позволяет вам изменять элементы, на которые он указывает, это полезно внутри методов класса const. Это также позволяет вам выразить свое намерение.
источник
Хорошо, позвольте мне сначала объяснить это на очень простом примере, не используя константный итератор. Рассмотрим коллекцию случайных целых чисел "randomData"
Как можно видеть для записи / редактирования данных внутри коллекции, используется обычный итератор, но для чтения используется постоянный итератор. Если вы попытаетесь использовать константный итератор в первом цикле for, вы получите ошибку. В качестве правила большого пальца используйте постоянный итератор для чтения данных внутри коллекции.
источник