To clarify, I am using the GCC7.1.0 compiler, and the standard library source code is also in this version.

This article describes the use and principles of array in STL.

takeaway

An array is a fixed-size array. The element type and size are specified when the array is declared.

template<typename _Tp, std::size_t _Nm>
    struct array
    {. };Copy the code

Some books say array is a class, but I see structs in this version, but that doesn’t matter. Structs are not very different from classes except in a few subtle ways. Array is a template class.

Initial use of array

To use array, include the header

and declare the STD namespace.

Here’s a simple example:

#include <array>
#include <iostream>

int main(a)
{
	std::array<int,5> a = {1.2.3.4.5};
	for(auto i:a)
	{
		std::cout << "value is " << i << std::endl;
	}
	return 0;
}
Copy the code

Use of fill() and swap()

Take a look at their prototype, as follows:

// Fill fills all elements of the current array with an input parameter __u
void fill(const value_type& __u);
// Swap swaps two arrays
void swap(array& __other) noexcept(_AT_Type::_Is_nothrow_swappable::value);
Copy the code

Take a look at the use case:

#include <array>
#include <iostream>

int main(a)
{
	std::array<int,5> arr1;
	arr1.fill(5);
	for(auto i:arr1)
	{
		std::cout << "arr1 value is " << i << std::endl;
	}
	std::array<int,5> arr2 = {1.2.3.4.5};
	arr2.swap(arr1);
	for(auto i:arr1)
	{
		std::cout << "arr1 value is " << i << std::endl;
	}
	for(auto i:arr2)
	{
		std::cout << "arr2 value is " << i << std::endl;
	}
	return 0;
}
Copy the code

A point to note here is that arr1 and arr2 element type and size must be completely consistent, only can use the swap function, because use the swap is the premise of type to be completely consistent, and is of type array container consists of two template parameters: the element type and element number, if not, there is no way through at compile time.

Iterator function

// Where value_type is the element type specified when defining an array, as in the example above, it is of type int
typedef value_type*          		      iterator;
typedef const value_type*			      const_iterator;
// Returns a writable iterator that refers to the first element of the current array
_GLIBCXX17_CONSTEXPR iterator
begin(a) noexcept
{ return iterator(data()); }
// Returns a read-only iterator to the first element of the current array
_GLIBCXX17_CONSTEXPR const_iterator
begin(a) const noexcept
{ return const_iterator(data()); }
// Returns a writable iterator that points to the position next to the last element of the current array
_GLIBCXX17_CONSTEXPR iterator
end(a) noexcept
{ return iterator(data() + _Nm); }
// Returns a read-only iterator that points to the position next to the last element of the current array
_GLIBCXX17_CONSTEXPR const_iterator
end(a) const noexcept
{ return const_iterator(data() + _Nm); }
// Returns a writable inversion iterator that points to the last element in the current array
_GLIBCXX17_CONSTEXPR reverse_iterator
rbegin(a) noexcept
{ return reverse_iterator(end()); }
// Returns a read-only iterator to the last element of the current array
_GLIBCXX17_CONSTEXPR const_reverse_iterator
rbegin(a) const noexcept
{ return const_reverse_iterator(end()); }
// Returns a writable iterator that refers to the preceding position of the first element in the current array
_GLIBCXX17_CONSTEXPR reverse_iterator
rend(a) noexcept
{ return reverse_iterator(begin()); }
// Returns a read-only iterator that points to the preceding position of the first element in the current array
_GLIBCXX17_CONSTEXPR const_reverse_iterator
rend(a) const noexcept
{ return const_reverse_iterator(begin()); }

// The following four iterators are the same as above, except that they are all read-only iterators
_GLIBCXX17_CONSTEXPR const_iterator
cbegin(a) const noexcept
{ return const_iterator(data()); }

_GLIBCXX17_CONSTEXPR const_iterator
cend(a) const noexcept
{ return const_iterator(data() + _Nm); }

_GLIBCXX17_CONSTEXPR const_reverse_iterator
crbegin(a) const noexcept
{ return const_reverse_iterator(end()); }

_GLIBCXX17_CONSTEXPR const_reverse_iterator
crend(a) const noexcept
{ return const_reverse_iterator(begin()); }
Copy the code

There are two things to note in this set of iterator functions:

  • For example, begin, the function name and parameter are the same, but the return type is not the same as const.
  • The second is to reverse the iterator, which is the iterator referring to the previous element in the current position.

To avoid confusion, use begin if you want to read and write, cbegin if you want to read only, and rbegin if you want to reverse.

The use cases are as follows:

#include <iostream>
#include <array>
using namespace std;

int main(a)
{
	array<int,5> a = {1.2.3.4.5};
	array<int,5>::iterator ite1 = a.begin(a); array<int,5>::const_iterator ite2 = a.begin(a);auto ite3 = a.rbegin(a); *ite1 =3;
	cout << *ite1 << endl;
	//*ite2 = 4; // Write data to read-only location
	cout << *ite2 << endl;
	cout << *ite3 << endl;
 
	return 0;
}
Copy the code

As you can see, the compiler should decide which function to call based on the type of the variable on the left. The * ITe3 output is 5, indicating that the position and orientation are reversed in Rbegin.

Size, max_size, empty functions

The function prototype is as follows:

constexpr size_type
size(a) const noexcept { return _Nm; }

constexpr size_type
max_size(a) const noexcept { return _Nm; }

constexpr bool
empty(a) const noexcept { return size() = =0; }
Copy the code

_Nm is a fixed value when an array is declared, indicating the number of its elements. Array is a container with a fixed capacity, so its size()=max_size(). When empty returns true, the container has no elements.

Subscript [] and at function

Take a look at the prototype:

// Overloading operator[] allows us to use array as an array
_GLIBCXX17_CONSTEXPR reference
operator[](size_type __n) noexcept
{ return _AT_Type::_S_ref(_M_elems, __n); }

constexpr const_reference
operator[](size_type __n) const noexcept
{ return _AT_Type::_S_ref(_M_elems, __n); }

_GLIBCXX17_CONSTEXPR reference
at(size_type __n)
{
if (__n >= _Nm)
std::__throw_out_of_range_fmt(__N("array::at: __n (which is %zu) "
                ">= _Nm (which is %zu)"),
            __n, _Nm);
return _AT_Type::_S_ref(_M_elems, __n);
}

constexpr const_reference
at(size_type __n) const
{
// Result of conditional expression must be an lvalue so use
// boolean ? lvalue : (throw-expr, lvalue)
return __n < _Nm ? _AT_Type::_S_ref(_M_elems, __n)
: (std::__throw_out_of_range_fmt(__N("array::at: __n (which is %zu) "
                   ">= _Nm (which is %zu)"),
               __n, _Nm),
 _AT_Type::_S_ref(_M_elems, 0));
}
Copy the code

The overloaded operator[] function and the at function both implement two. Like the iterator above, they use an lvalue to determine which function to call.

[] does not raise an error if the index [] is not in the scope of the container. The at function does not raise an error if the index [] is in the scope of the container.

#include <iostream>
#include <array>
using namespace std;

int main(a)
{
	array<int,5> a= {1.2.3.4.5};
	cout << a[6];// There is no error here
	//cout << a.at(6); Terminate called after throwing an instance of 'STD ::out_of_range'
    a[3] = 100;
	cout << "a[3]=" << a[3] << endl;
	return 0;
}
Copy the code

Therefore, you can use [] if you can ensure that it will not exceed the scope of the container. Otherwise, you are advised to use AT to avoid some puzzling problems.

In addition, since [] and at return references, we can directly modify the values of the elements in the array using these two methods.

Front, back, data functions

The array container doesn’t have functions like push to write data to it because it has a fixed capacity, but it does provide functions to fetch data from the beginning and end:

_GLIBCXX17_CONSTEXPR reference
front(a) noexcept
{ return *begin(a); }constexpr const_reference
front(a) const noexcept
{ return _AT_Type::_S_ref(_M_elems, 0); }

_GLIBCXX17_CONSTEXPR reference
back(a) noexcept
{ return_Nm ? * (end() - 1) : *end(a); }constexpr const_reference
back(a) const noexcept
{
return _Nm ? _AT_Type::_S_ref(_M_elems, _Nm - 1)
       : _AT_Type::_S_ref(_M_elems, 0);
}

_GLIBCXX17_CONSTEXPR pointer
data(a) noexcept
{ return _AT_Type::_S_ptr(_M_elems); }

_GLIBCXX17_CONSTEXPR const_pointer
data(a) const noexcept
{ return _AT_Type::_S_ptr(_M_elems); }
Copy the code

If you look closely, you will see that front and back return references, which are consistent with the type of subscript and at, while data return Pointers and iterators, so you can refer to the above code for their use. I won’t go into details here.

Implementation principles of array

We said that array is a fixed size array, so how is it implemented?

The array header is defined as follows:

template<typename _Tp, std::size_t _Nm>
struct __array_traits
{
  typedef _Tp _Type[_Nm];
  typedef __is_swappable<_Tp> _Is_swappable;
  typedef __is_nothrow_swappable<_Tp> _Is_nothrow_swappable;

  static constexpr _Tp&
  _S_ref(const _Type& __t, std::size_t __n) noexcept
  { return const_cast<_Tp&>(__t[__n]); }

  static constexpr _Tp*
  _S_ptr(const _Type& __t) noexcept
  { return const_cast<_Tp*>(__t); }};template<typename _Tp, std::size_t _Nm>
struct array
{.// Alias the type here
    typedef _GLIBCXX_STD_C::__array_traits<_Tp, _Nm> _AT_Type;
    typename_AT_Type::_Type _M_elems; . };Copy the code

_M_elems is an array defined by the number of elements we specify. The element type and the number of elements are determined by the template arguments we declare the array object, and the return reference or pointer is determined by the static member functions _S_ref and _S_ptr.

Array to put it bluntly, is in a fixed size array based on some of the packaging, and using the template, allows us to define the various types of flexible arrays, since this is an array, it must be a contiguous address space in a contiguous address space, whether to get the data or modify the data can be completed under the constant complexity, So array efficiency is pretty good. Compared with ordinary arrays, array is encapsulated and can only be accessed through the interface provided by array, which ensures certain security. Therefore, if you want to use arrays of fixed size, you are recommended to use Array.

If my article is useful to you, please give it a thumbs up.