Відповіді:
Це досить просто. Скажіть, у мене вектор:
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_iterator
s, і тому 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, що повертає постійний ітератор