Ответы:
Это довольно просто. Скажем, у меня есть вектор:
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))
Это просто возвращает 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
общего кода, где мы не можем контролировать используемый тип, но с помощью range- 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 даже из неконстантного контейнера
Они привели этот пример
vector<MyType> v;
// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
// use *it ...
}
Однако, когда обход контейнера предназначен только для проверки, обычно предпочтительнее использовать const_iterator, чтобы позволить компилятору диагностировать нарушения правильности const
Обратите внимание , что рабочий документ , также упоминается адаптер шаблонов, которые теперь были завершены , как std::begin()
и std::end()
и , которые также работают с носителями массивов. Соответствующие std::cbegin()
и, std::cend()
как ни странно, отсутствуют на этот раз, но они также могут быть добавлены.
Просто наткнулся на этот вопрос ... Я знаю, что он уже ответил, и это просто боковой узел ...
auto const it = container.begin()
это другой тип, то auto it = container.cbegin()
разница для int[5]
(используя указатель, который, я знаю, не имеет метода begin, но хорошо показывает разницу ... но будет работать в 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
главное преимущество - это производительность (а это не так: это семантически правильный и безопасный код). Но, хотя у вас есть точка зрения, (А) auto
делает это не проблема; (B) говоря о производительности, вы упустили главное, что вы должны были здесь сделать: кэшировать end
итератор, объявив его копию в init-условии for
цикла, и сравнить с этим, вместо того, чтобы получать новую копию с помощью значение для каждой итерации. Это сделает вашу точку зрения лучше. : P
const
определенно может помочь достичь более высокой производительности не из-за некоторого волшебства в самом const
ключевом слове, а потому, что компилятор может включить некоторые оптимизации, если он знает, что данные не будут изменены, что было бы невозможно в противном случае. Проверьте этот бит из выступления Джейсона Тернера для живого примера этого.
const
может (почти косвенно) привести к повышению производительности; на тот случай, если кто-то прочитает это, возможно, подумает: «Я не буду беспокоиться о добавлении, const
если сгенерированный код никогда не будет затронут», что неверно.
это просто, cbegin возвращает постоянный итератор, где begin возвращает просто итератор
для лучшего понимания давайте рассмотрим два сценария
сценарий - 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, которые возвращают постоянный итератор