Відповіді:
Це досить просто. Скажіть, у мене вектор:
std::vector<int> vec;
Я заповнюю це деякими даними. Тоді я хочу отримати до нього кілька ітераторів. Можливо, передайте їх навколо. Можливо, щоб std::for_each:
std::for_each(vec.begin(), vec.end(), SomeFunctor());
У C ++ 03 SomeFunctorбуло вільно мати змогу змінювати параметр, який він отримує. Звичайно, SomeFunctorможе взяти його параметр за значенням або за const&, але немає способу забезпечити це. Не без цього робити щось нерозумно:
const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());
Тепер ми представляємо cbegin/cend:
std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());
Тепер у нас є синтаксичні запевнення, які SomeFunctorне можуть змінювати елементи вектора (без звіту, звичайно). Ми явно отримуємо const_iterators, і тому SomeFunctor::operator()будемо називатися з const int &. Якщо він приймає параметри так int &, C ++ видасть помилку компілятора.
C ++ 17 має більш елегантне рішення цієї проблеми: std::as_const. Ну, принаймні, це елегантно при використанні на основі діапазону for:
for(auto &item : std::as_const(vec))
Це просто повертає a const&об'єкту, який він надається.
std::cbegin/cendвільних функцій, як std::begin/std::endіснують. Це був нагляд з боку комітету. Якби ці функції існували, зазвичай це був би спосіб їх використання.
std::cbegin/cendбуде додано в C ++ 14. Дивіться en.cppreference.com/w/cpp/iterator/begin
for(auto &item : std::as_const(vec))еквівалентно for(const auto &item : vec)?
constпосилання. Ніколь розглядає контейнер як const, тому autoвиводить constпосилання. ІМО auto const& itemпростіший і зрозуміліший. Незрозуміло, чому std::as_const()тут добре; Я можу побачити, що це буде корисно при передачі чогось не- constзагального коду, де ми не можемо керувати типом, який звикає, але з діапазоном for- ми можемо, тому мені просто здається, що там додається шум.
Поза тим, що сказав Нікол Болас у своїй відповіді , розгляньте нове autoключове слово:
auto iterator = container.begin();
З auto, немає ніякого способу переконатися, що begin()повертає постійного оператора для непостійної посилання на контейнер. Отже, тепер ви робите:
auto const_iterator = container.cbegin();
const_iterator- це ще один ідентифікатор. Жодна версія не використовує пошук звичайних членів typedefs decltype(container)::iteratorабо decltype(container)::const_iterator.
const_iteratorдопомогою auto: Напишіть шаблон допоміжної функції, покликаний make_constкваліфікувати аргумент об'єкта.
Візьміть це як практичну корисну справу
void SomeClass::f(const vector<int>& a) {
auto it = someNonConstMemberVector.begin();
...
it = a.begin();
...
}
Присвоєння не вдається, тому що itце ітератор, що не має чинності. Якщо ви використовували cbegin спочатку, ітератор мав би правильний тип.
Від http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
так що програміст може безпосередньо отримати const_iterator навіть з контейнера, який не містить const
Вони дали цей приклад
vector<MyType> v;
// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
// use *it ...
}
Однак, коли обхід контейнера призначений лише для огляду, загалом бажаною практикою є використання const_iterator, щоб дозволити компілятору діагностувати порушення коректності коректності.
Зверніть увагу , що робочий документ , також згадується адаптер шаблонів, які тепер були завершені , як std::begin()і std::end()і , які також працюють з носіями масивів. Відповідних std::cbegin()і std::cend()цікаво відсутні на цей час, але вони також можуть бути додані.
Щойно натрапив на це питання ... Я знаю, що на це відповів Алреді, і це лише бічний вузол ...
auto const it = container.begin() тоді інший тип auto it = container.cbegin()
різниця для int[5](використовуючи покажчик, для якого я знаю, немає методу початку, але добре показує різницю ... але він би працював у c ++ 14 для std::cbegin()і std::cend(), що по суті є тим, що слід використовувати, коли він знаходиться тут) ...
int numbers = array[7];
const auto it = begin(numbers); // type is int* const -> pointer is const
auto it = cbegin(numbers); // type is int const* -> value is const
iteratorі const_iteratorмають спадкове відношення, і неявна конверсія відбувається в порівнянні з іншим типом або присвоєною їм.
class T {} MyT1, MyT2, MyT3;
std::vector<T> MyVector = {MyT1, MyT2, MyT3};
for (std::vector<T>::const_iterator it=MyVector.begin(); it!=MyVector.end(); ++it)
{
// ...
}
Використання cbegin()та cend()збільшить продуктивність у цьому випадку.
for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
// ...
}
constголовна перевага якого - це продуктивність (що це не так: це семантично правильний і безпечний код). Але, хоча у вас є точка, (A) autoробить це не питанням; (B) говорячи про продуктивність, ви пропустили головне, що ви мали б зробити тут: кешуйте endітератор, оголосивши його копію в init-стані forциклу, і порівняйте з цим, замість того, щоб отримати нову копію значення для кожної ітерації. Це зробить вашу думку краще. : P
constбезумовно може допомогти досягти кращої продуктивності не через якусь магію самого constключового слова, а тому, що компілятор може ввімкнути деякі оптимізації, якщо знає, що дані не будуть змінені, що інакше було б неможливо. Перегляньте цей біт із розмови Джейсона Тернера, щоб мати приклад цього.
constможе (майже опосередковано) призвести до корисних результатів; на всякий випадок, коли хтось, читаючи це, може подумати, "я не буду турбувати додавання, constякщо генерований код ніколи не вплине", що не відповідає дійсності.
його простий, cbegin повертає постійний ітератор, де start повертає просто ітератор
для кращого розуміння давайте тут візьмемо два сценарії
сценарій - 1:
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.begin();i< v.end();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
це запуститься, тому що тут ітератор i не є постійним і може бути збільшений на 5
тепер давайте скористаємося cbegin і перестане позначати їх як постійний сценарій ітераторів - 2:
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.cbegin();i< v.cend();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
це не буде працювати, тому що ви не можете оновити значення за допомогою cbegin і cend, що повертає постійний ітератор