C++'s iterators are a generalisation of pointers-into-arrays. So you can write this:
void sort_array() {
int arr[10] = {4, 5, 1, 7, 6, 3, 9, 2, 8, 0};
std::sort(arr, arr + 10); // std::sort<int*>
}
just as you can write
void sort_vector() {
std::vector<int> v = {4, 5, 1, 7, 6, 3, 9, 2, 8, 0};
std::sort(v.begin(), v.end()); // std::sort<std::vector<int>::iterator>
}
And in general you can write
template <Container C, Integral T> // not-yet-legal concepts syntax
sort_container() {
C<T> c = {4, 5, 1, 7, 8, 3, 9, 2, 8, 0};
std::sort(std::begin(c), std::end(c));
}
Note: template <Container C, Integral T>
means template <typename C, typename T> requires Container<C> && Integral<T>
, which roughly means template <typename C, typename T, std::enable_if_t<std::is_integral_v<T> && std::is_container_v<C>>
, if std::is_container
existed
std::sort
, because it is a function template, works for both T*
(the sort of iterator you use into a T[N]
) and std::vector<T>::iterator
(the sort of iterator you use for a std::vector<T>
, obviously). This only works because iterators are designed to mimic how pointers work.
And you can write these:
for (auto x : A) {
std::cout << x << std::endl;
}
which are transformed into this (basically):
auto __end = std::end(A);
for (auto __it = std::begin(A); __it != __end; ++__it) {
std::cout << (*__it) << std::endl;
}
Which works whether A
is arr
or v
.
Note: While in C you use T *
everywhere, in modern C++ you use unique_ptr<T>
for a owning-pointer-to-T
, T *
for a non-owning-nullable-pointer-toT
, T&
for a non-owning-non-nullable-pointer-to-T, std::array<T,N>::iterator
for a pointer-into-std::array<T,N>
(the replacement for T[N]
) and std::vector<T>::iterator
for a pointer-into-std::vector<T>
(the replacement for T[]
).
But iterators don't have to have the semantics of pointers, otherwise we'd just use pointers.