Getting Started

In this example, we initialize a 2-dimensional array of numbers, and then we inspect the properties of this array, copy the array, and manipulate the element values. The example can be followed online on Compiler Explorer.

The individual elements are initialized from a nested rectangular list.

multi::array<int, 2> A = {
    {1, 2, 3},
    {4, 5, 6},
};

auto const [n, m] = A.sizes();

assert( n == 2 );  // or std::get<0>(A.sizes()) == 2
assert( m == 3 );  // or std::get<1>(A.sizes()) == 3

assert( A.size() == 2 );  // size in first dimension, same as std::get<0>(A.sizes())

Individual elements can be accessed by the multidimensional indices, using brackets.

assert( A[1][2] == 6 );

Arrays can be viewed simultaneously as a nested array or as flat sequence of elements.

assert( A.elements().size() == 6 );  // total number of elements

assert( A.elements()[6] == 6 );
assert( &A.elements()[6] == &A[1][2] );

The elements also can be accessed through iterators; A.begin(), A.begin()→begin() and A.elements().begin() are examples of valid ways to access the data structure that allow the interaction with algorithms.

The value of an array can be copied, (moved,) and compared; copies are equal but independent (disjoint).

multi::array<int, 2> B = A;                 // we make a copy

assert(  B              ==  A              );  // copies are equal
assert(  B.extensions() ==  A.extensions() );  // extensions (sizes) are equal
assert(  B[0][1]        ==  A[0][1]        );  // all elements are equal
assert( &B[0][1]        != &A[0][1]        );  // elements are independent (different addresses)

An array can be initialized from its sizes alone, in which case the element values are defaulted:

multi::array<int, 3> C({3, 4, 5});  // 60 elements with unspecified values
assert( C.elements().size() == 3*4*5 );  // or C.num_elements()

Arrays can be passed by value or by reference. Most of the time, arguments should be passed through generic parameters to also allow functions to work with parts (subblocks, slices, etc.) of an array. The most useful functions work on the concept of an array rather than on a concrete type, for example:

template<class ArrayOfInts2D>  // instead of the overspecific argument multi::array<double, 2>
auto element_1_1(ArrayOfInts2D const& m) -> int const& { return m[1][1]; }
...
assert( &element_1_1(A) == &A[1][1] );

The function expects any array or subarray of dimension 2 (or greater) and returns an element with type int.

The generic function template arguments that are not intended to be modified are passed by const&; otherwise, they are passed by forward-reference &&. In this way, the functions can be also applied to subblocks of larger matrices.

assert( &element_1_1(C[0]) == &C[0][1][1] );

Although most of the examples use numeric elements for conciseness, the library is designed to hold general types e.g. non-numeric, non-trivial types, like std::string, other containers or, in general, user-defined value-types.