Skip to content

const_iterator

在学习cbegincend的时候,发现了const_iterator

What's new in C++?

从下面的文章来看,C++11引入const_iterator的目的是:

保证type safety: const-correctness。

以pointer来进行类比

参见 stackoverflow What is the difference between const_iterator and non-const iterator in the C++ STL? # A,下面收录了,简而言之:

pointer iterator explanation
const T* std::vector<T>::const_iterator A non-const iterator to a const element

即: 等价于 pointer to const。

stackoverflow What is the reason behind cbegin/cend?

I wonder why cbegin and cend were introduced in C++11?

What are cases when calling these methods makes a difference from const overloads of begin and end?

A

It's quite simple. Say I have a vector:

std::vector<int> vec;

I fill it with some data. Then I want to get some iterators to it. Maybe pass them around. Maybe to std::for_each :

std::for_each(vec.begin(), vec.end(), SomeFunctor());

In C++03, SomeFunctor was free to be able to modify the parameter it gets. Sure, SomeFunctor could take its parameter by value or by const&, but there's no way to ensure that it does. Not without doing something silly like this:

NOTE: 让compiler保证能够检查出违反type safety的情况,这是C++的发展发现之一,其实这是在type safety方向的演进。

const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());

Now, we introduce cbegin/cend:

std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());

Now, we have syntactic assurances that SomeFunctor cannot modify the elements of the vector (without a const-cast, of course). We explicitly get const_iterators, and therefore SomeFunctor::operator() will be called with const int &. If it takes it's parameters as int &, C++ will issue a compiler error.

NOTE:

#include <iostream>
#include <vector>
#include <algorithm>
void SomeFunctor(int& i)
{
  std::cout << i << std::endl;
}
int main()
{
  std::vector<int> vec { 1, 2, 3 };
  const std::vector<int> &vec_ref = vec;
  std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor);
}
// g++ --std=c++11 test.cpp

上述代码编译报错如下:

In file included from /usr/include/c++/4.8.2/algorithm:62:0,
                 from test.cpp:3:
/usr/include/c++/4.8.2/bits/stl_algo.h: In instantiation of _Funct std::for_each(_IIter, _IIter, _Funct) [with _IIter = __gnu_cxx::__normal_iterator<const int*, std::vector<int> >; _Funct = void (*)(int&)]:
test.cpp:12:59:   required from here
/usr/include/c++/4.8.2/bits/stl_algo.h:4417:14: error: invalid initialization of reference of type int& from expression of type const int
  __f(*__first);

从上述代码可以看出,函数调用所执行的是:

__f(*__first);

由于*first的type是const int,而函数 SomeFunctor(int& i) 的入参是 int&,显然这是违背 const-correctness 的,这就导致了compiler error。

需要注意的是,pass-by-value是不违背的:

#include <iostream>
#include <vector>
#include <algorithm>
void SomeFunctor(int i)
{
  std::cout << i << std::endl;
}
int main()
{
  std::vector<int> vec { 1, 2, 3 };
  const std::vector<int> &vec_ref = vec;
  std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor);
}
// g++ --std=c++11 test.cpp

上述代码是编译通过的。


C++17 has a more elegant solution to this problem: std::as_const. Well, at least it's elegant when using range-based for:

for(auto &item : std::as_const(vec))

This simply returns a const& to the object it is provided.

@NicolBolas is for(auto &item : std::as_const(vec)) equivalent to for(const auto &item : vec)? – luizfls Oct 18 '17 at 20:34

@luizfls Yes. Your code says the item will not be modified by putting the const on the reference. Nicol's views the container as const, so auto deduces a const reference. IMO auto const& item is easier and clearer. It's unclear why std::as_const() is good here; I can see it'd be useful when passing something non-const to generic code where we can't control the type that gets used, but with range-for, we can, so it just seems like added noise to me there. – underscore_d Sep 24 '18 at 21:00

stackoverflow What is the difference between const_iterator and non-const iterator in the C++ STL?

A

const_iterators don't allow you to change the values that they point to, regular iterators do.

As with all things in C++, always prefer const, unless there's a good reason to use regular iterators (i.e. you want to use the fact that they're not const to change the pointed-to value).

A

NOTE: 使用point来进行类比,非常容易理解。

They should pretty much be self-explanatory. If iterator points to an element of type T, then const_iterator points to an element of type 'const T'.

It's basically equivalent to the pointer types:

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
pointer iterator explanation
T* std::vector<T>::iterator A non-const iterator to a non-const element
T* const const std::vector<T>::iterator A const iterator to a non-const element
const T* std::vector<T>::const_iterator A non-const iterator to a const element

A const iterator always points to the same element, so the iterator itself is const. But the element it points to does not have to be const, so the element it points to can be changed.

A const_iterator is an iterator that points to a const element, so while the iterator itself can be updated (incremented or decremented, for example), the element it points to can not be changed.

stackoverflow What is the difference between cbegin and begin for vector?

The member begin has two overloadings one of them is const_iterator begin() const;. There's also the cbegin const_iterator cbegin() const noexcept;. Both of them returns const_iterator to the begin of a list. What's the difference?

A

begin will return an iterator or a const_iterator depending on the const-qualification of the object it is called on.

cbegin will return a const_iterator unconditionally.

std::vector<int> vec;
const std::vector<int> const_vec;

vec.begin(); //iterator
vec.cbegin(); //const_iterator

const_vec.begin(); //const_iterator
const_vec.cbegin(); //const_iterator

如何获得?

通过 cbegincend