Search

6.15 — An introduction to std::array

In previous lessons, we’ve talked at length about fixed and dynamic arrays. Although both are built right into the C++ language, they both have downsides: Fixed arrays decay into pointers, losing the array length information when they do, and dynamic arrays have messy deallocation issues and are challenging to resize without error.

To address these issues, the C++ standard library includes functionality that makes array management easier, std::array and std::vector. We’ll examine std::array in this lesson, and std::vector in the next.

An introduction to std::array in C++11

Introduced in C++11, std::array provides fixed array functionality that won’t decay when passed into a function. std::array is defined in the array header, inside the std namespace.

Declaring a std::array variable is easy:

Just like the native implementation of fixed arrays, the length of a std::array must be set at compile time.

std::array can be initialized using an initializer lists or uniform initialization:

Unlike built-in fixed arrays, with std::array you can not omit the array length when providing an initializer:

You can also assign values to the array using an initializer list

Accessing array values using the subscript operator works just like you would expect:

Just like built-in fixed arrays, the subscript operator does not do any bounds-checking. If an invalid index is provided, bad things will probably happen.

std::array supports a second form of array element access (the at() function) that does bounds checking:

In the above example, the call to array.at(1) checks to ensure array element 1 is valid, and because it is, it returns a reference to array element 1. We then assign the value of 6 to this. However, the call to array.at(9) fails because array element 9 is out of bounds for the array. Instead of returning a reference, the at() function throws an error that terminates the program (note: It’s actually throwing an exception of type std::out_of_range -- we cover exceptions in chapter 15). Because it does bounds checking, at() is slower (but safer) than operator[].

std::array will clean up after itself when it goes out of scope, so there’s no need to do any kind of cleanup.

Size and sorting

The size() function can be used to retrieve the length of the array:

This prints:

length: 5

Because std::array doesn’t decay to a pointer when passed to a function, the size() function will work even if you call it from within a function:

This also prints:

length: 5

Note that the standard library uses the term “size” to mean the array length — do not get this confused with the results of sizeof() on a native fixed array, which returns the actual size of the array in memory (the size of an element multiplied by the array length). Yes, this nomenclature is inconsistent.

Also note that we passed std::array by (const) reference. This is to prevent the compiler from making a copy of the array when the array was passed to the function (for performance reasons).

Rule: Always pass std::array by reference or const reference

Because the length is always known, for-each (ranged for) loops work with std::array:

You can sort std::array using std::sort, which lives in the algorithm header:

This prints:

1 3 5 7 9

The sorting function uses iterators, which is a concept we haven’t covered yet, so for now you can treat the parameters to std::sort() as a bit of magic. We’ll explain them in the lesson on iterators.

Summary

std::array is a great replacement for build-in fixed arrays. It’s efficient, in that it doesn’t use any more memory than built-in fixed arrays. The only real downside of a std::array over a built-in fixed array is a slightly more awkward syntax, and that you have to explicitly specify the array length (the compiler won’t calculate it for you from the initializer). But those are minor quibbles — we recommend using std::array over built-in fixed arrays for any non-trivial use.

6.16 -- An introduction to std::vector
Index
6.14 -- Pointers to pointers and dynamic multidimensional arrays

90 comments to 6.15 — An introduction to std::array

  • Bryan

    Hello Alex,

    I have a simple question.
    When defining a function that takes a std::array as parameter,
    we need to put in the type and length between the <type, length>.

    However when I declare a function, I cannot use a const int as the length.
    I fixed this by using a macro instead.

    Is using a macro the correct way to work around this? (as in would this be the best programming practice)
    Or is there another way to use a const int instead?

    Example:

    • Mates

      I am not quite sure I understand your question well, but I think it would work if you put the initialization of ‘arraylength’ before the declaration of ‘doSomething’. Like this:

      At least for me this compiles fine.

      • Bryan

        Thanks for trying to give an answer.

        While your code compiles correctly it has a "problem".
        That is the

        part.
        This is declared out of scope, and thus is a global variable.
        A global variable is unnecessary memory usage.

        My question is focussed on an alternative to Macro’s, by being able to
        use a variable declared inside of the scope, or atleast avoid resorting to using global variables.

        • Mates

          I don’t think that global variables use more memory than local ones. The memory is just used throughout the whole program, which is probably what you need in this case, as you use it both functions. And why do you want to avoid constant global variables? If I understood the tutorial about them well, only the non-constant ones are evil.

    • Alex

      arraylength is a special type of parameter called a template expression parameter. This parameter has type std::size_t, which is usually defined as some kind of unsigned int. The compiler isn’t expecting a type here -- it’s expecting a value (such as 50). I’m a bit surprised defining a const int variable works -- the compiler must know that you’ve given the const int an initializer of 50, so it’s just substituting that 50 as the second parameter of std::array for you.

  • Mates

    Hello Alex,
    recently, I came across something interesting. In this:

    In this example, the array ‘x’ is created inside the ‘returnA’ function as a local variable. That means it should be destroyed at the end of ‘returnA’. But I return it and the cout prints 1 and 2. Is this because I missed something from the tutorial, some lucky undefined behavior, or is my code simply right?
    Best regards, Mates

    • Alex

      When you return x, you’re returning returnA’s x value back to main(), and this happens before returnA’s x is destroyed. That return value is used to initialize main’s x. Then returnA’s x is destroyed.

      So there’s actually no problem here -- main is accessing its own local copy of the array, which exists independently of returnA’s x that gets destroyed.

  • Taksh

    Hey Alex,

    You wrote:

    In the for each loop you defined &element as const. Then how it is able to change its value on every iteration ?

    Thanks in advance.

    • Alex

      The element is created and initialized with each loop iteration, and destroyed at the end of each loop iteration. It is essentially a local variable to the loop.

  • umang

    int main()
    {
      std::array<int,5> myarray{1,2,3,4,5};
      int *ptr=myarray;
      std::cout<<*(ptr+2);

      return 0;
    }
    why is this incorrect?

    • Alex

      myarray is a std::array, and std::array doesn’t have a conversion to int*. This means you can’t assign an integer pointer to point at it.

  • lnm

    Can you tell me what algorithm does std::sort use ?

  • Omri

    Indeed I was thinking in terms of functions.Thank you for clarifying this.

  • Omri

    Hello Alex,
    Thank you for the reply.
    As I understood matters up to here, we #include header (.h ) files only, and these include function declarations, and do not include function definitions/implementations (which, when up to us, we place in .cpp files). This is good enough for correct compilation of single files.
    For linkage it is up to the linker to find the function definition/implementation when the code calls/uses the declared functions. The linker uses its "magic" search capabilities to find the definition of the function in .cpp files residing in the folder "housing" the calling file, or elswhere in what is somewhat vaguely refered to as "system files". All this, if correct, is difficult to accurately go along with "…std::array is defined in the array header, inside the std namespace".
    Thank you Again.

    • Alex

      Your understanding is good, but incomplete. You’re focused on functions here, but std::array is not a function -- it’s a type. And types play by different rules.

      Generally, it’s not enough to declare a type in a header -- with a few exceptions, types are more often defined in headers, and then included into whatever files need them. So the std::array type is defined in the array header.

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter