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 std::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 std::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 std::array when the std::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.

Manually indexing std::array via size_type

Pop quiz: What’s wrong with the following code?

The answer is that there’s a likely signed/unsigned mismatch in this code! Due to a curious decision, the size() function and array index parameter to operator[] use a type called size_type, which is defined by the C++ standard as an unsigned integral type. Our loop counter/index (variable i) is a signed int. Therefore both the comparison i < myArray.size() and the array index myArray[i] have type mismatches.

Interestingly enough, size_type isn't a global type (like int or std::size_t). Rather, it's defined inside the definition of std::array (C++ allows nested types). This means when we want to use size_type, we have to prefix it with the full array type (think of std::array acting as a namespace in this regard). In our above example, the fully-prefixed type of "size_type" is std::array<int, 5>::size_type!

Therefore, the correct way to write the above code is as follows:

That's not very readable. A type alias can help a bit:

This is a bit better, and this solution probably has the best combination of technical correctness and readability.

In all common implementations of std::array, size_type is a typedef for std::size_t. So it's somewhat common to see developers use size_t instead. While not technically correct, in almost all implementations, this will work:

A better solution is to avoid manual indexing of std::array in the first place. Instead, use range-based for loops (or iterators) if possible.

Summary

std::array is a great replacement for built-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, that you have to explicitly specify the array length (the compiler won’t calculate it for you from the initializer), and the signed/unsigned issues with size and indexing. But those are comparatively minor quibbles — we recommend using std::array over built-in fixed arrays for any non-trivial array use.

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

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

  • darshan

    Why does

    give an error?

  • Why i am getting error in the above program?
    But in this one.

    • Snippet 1
      * Line 5: Initialize your variables with brace initializers.
      * Line 2: Limit your lines to 80 characters in length for better readability on small displays.

      Snippet 2
      * Line 5: Initialize your variables with brace initializers.

      If you're getting errors, post the error message.
      You're using @std::string without including <string>.

      • Oh! I actually never included string library unless i had to do string operations as it worked all the time and this is the first time i used std::string  in global scope. It worked when i included string library. But it also works when i initialise keywords array inside the main even without using string library.

        Why this happens ?
        Thanks a lot for the suggestions. :)

  • Shri

    Hi Alex, in this chapter you have mentioned that one of the reasons to use std::array is that, when passing std:array to a function (where the function parameter is a reference to the argument), it does not decay into a pointer.

    But in later chapters you show that even built-in arrays can be passed by reference (along with mentioning the length of array) such that the argument does not decay into a pointer.

    Based on this, do you think this advantage (std:array not decaying into a pointer when passed by reference into a function) should be removed from this chapter?

Leave a Comment

Put all code inside code tags: [code]your code here[/code]