Відповіді:
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. Але елемент, на який він вказує, не повинен бути const, тому елемент, на який він вказує, може бути змінений. Const_iterator - це ітератор, який вказує на елемент const, тому, хоча сам ітератор може бути оновлений (наприклад, нарощений або зменшений), елемент, на який він вказує, не може бути змінений.
const iterater
та const_iterator
.
На жаль, багато методів для контейнерів STL приймають ітератори замість const_iterators як параметри. Отже, якщо у вас є const_iterator , ви не можете сказати "вставити елемент перед елементом, на який вказує цей ітератор" (мовляв, така думка концептуально не є порушенням const). Якщо ви все-таки хочете зробити це, вам доведеться перетворити його на ітератор без const за допомогою std :: advance () або boost :: next () . Напр. boost :: next (container.begin (), std :: відстань (container.begin (), the_const_iterator_we_want_to_unconst)) . Якщо контейнер - це список std :: list , то час виконання цього виклику буде O (n) .
Тож універсальне правило додавати const там, де це "логічно" робити, є менш універсальним, якщо мова йде про контейнери STL.
Однак бутові контейнери приймають const_iterators (наприклад, boost :: unordered_map :: erase ()). Тож, коли ви використовуєте збільшити контейнери, ви можете бути «агресивними». До речі, хтось знає, чи або коли будуть виправлені контейнери STL?
vector
та deque
вставлення одного елемента недійсні всі існуючі ітератори, що не дуже const
. Але я бачу вашу думку. Такі операції захищені контейнером const
, а не ітераторами. І мені цікаво, чому в стандартному інтерфейсі контейнера не існує функції перетворення const-to-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 та non-const, ви можете легко конвертувати з non-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 кожного разу, коли ви можете їх використовувати (коли вам не потрібно змінювати контейнер з ними), щоб краще документувати свій намір читати, не змінюючи.
(як вже говорили інші) 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;
Як видно для запису / редагування даних всередині колекції, використовується звичайний ітератор, але для читання використовується постійний ітератор. Якщо ви спершу спробуєте використовувати постійний ітератор для циклу, ви отримаєте помилку. Як правило, використовуйте постійний ітератор для читання даних всередині колекції.