У чому причина cbegin / cend?


Відповіді:


227

Це досить просто. Скажіть, у мене вектор:

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&об'єкту, який він надається.


1
Я думав, що новий протокол - це cbegin (vec), а не vec.cbegin ().
Kaz Dragon

20
@Kaz: Не існує std::cbegin/cendвільних функцій, як std::begin/std::endіснують. Це був нагляд з боку комітету. Якби ці функції існували, зазвичай це був би спосіб їх використання.
Нікол Болас

20
Мабуть, std::cbegin/cendбуде додано в C ++ 14. Дивіться en.cppreference.com/w/cpp/iterator/begin
Аді Шавіт

8
@NicolBolas for(auto &item : std::as_const(vec))еквівалентно for(const auto &item : vec)?
luizfls

8
@luizfls Так. У вашому коді зазначено, що елемент не буде змінено, додавши constпосилання. Ніколь розглядає контейнер як const, тому autoвиводить constпосилання. ІМО auto const& itemпростіший і зрозуміліший. Незрозуміло, чому std::as_const()тут добре; Я можу побачити, що це буде корисно при передачі чогось не- constзагального коду, де ми не можемо керувати типом, який звикає, але з діапазоном for- ми можемо, тому мені просто здається, що там додається шум.
підкреслюй_d

66

Поза тим, що сказав Нікол Болас у своїй відповіді , розгляньте нове autoключове слово:

auto iterator = container.begin();

З auto, немає ніякого способу переконатися, що begin()повертає постійного оператора для непостійної посилання на контейнер. Отже, тепер ви робите:

auto const_iterator = container.cbegin();

2
@allyourcode: Не допомагає. Для компілятора const_iterator- це ще один ідентифікатор. Жодна версія не використовує пошук звичайних членів typedefs decltype(container)::iteratorабо decltype(container)::const_iterator.
aschepler

2
@aschepler Я не розумію вашого другого речення, але я думаю, що ви пропустили "const" перед "auto" в моєму питанні. Що б авто не прийшло, здається, що const_iterator повинен бути const.
allyourcode

26
@allyourcode: Це дасть вам постійний ітератор, але він дуже відрізняється від ітератора до постійних даних.
aschepler

2
Існує простий спосіб гарантувати, що ви отримуєте за const_iteratorдопомогою auto: Напишіть шаблон допоміжної функції, покликаний make_constкваліфікувати аргумент об'єкта.
Коламбо

17
Можливо, я просто не перебуваю в режимі мислення C ++, але я не бачу зв'язку між поняттями "простий спосіб" та "написати шаблон допоміжної функції". ;)
Стефан Маєвський

15

Візьміть це як практичну корисну справу

void SomeClass::f(const vector<int>& a) {
  auto it = someNonConstMemberVector.begin();
  ...
  it = a.begin();
  ...
}

Присвоєння не вдається, тому що itце ітератор, що не має чинності. Якщо ви використовували cbegin спочатку, ітератор мав би правильний тип.


8

Від 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()цікаво відсутні на цей час, але вони також можуть бути додані.


5

Щойно натрапив на це питання ... Я знаю, що на це відповів Алреді, і це лише бічний вузол ...

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

2

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
підкреслення_d

@underscore_d constбезумовно може допомогти досягти кращої продуктивності не через якусь магію самого constключового слова, а тому, що компілятор може ввімкнути деякі оптимізації, якщо знає, що дані не будуть змінені, що інакше було б неможливо. Перегляньте цей біт із розмови Джейсона Тернера, щоб мати приклад цього.
brainplot

@brainplot Я не сказав, що не міг. Я сказав, що це не його головна перевага, і я вважаю, що це завищено, коли справжня користь від нього - це семантично правильний і безпечний код.
підкреслюйте_d

@underscore_d Так, я згоден з цим. Я просто робив це явно, що constможе (майже опосередковано) призвести до корисних результатів; на всякий випадок, коли хтось, читаючи це, може подумати, "я не буду турбувати додавання, constякщо генерований код ніколи не вплине", що не відповідає дійсності.
brainplot

0

його простий, 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, що повертає постійний ітератор

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.