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

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 known at compile time.

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

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

However, since C++17, it is allowed to omit the type and size. They can only be omitted together, but not one or the other, and only if the array is explicitly initialized.

We favor this syntax rather than typing out the type and size at the declaration. If your compiler is not C++17 capable, you need to use the explicit syntax instead.

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 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 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 manual 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).


Always pass std::array by reference or const reference

Because the length is always known, range-based 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 later.

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. Fortunately, std::array::size_type is just an alias for std::size_t, so we can use that instead.

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.

Keep in mind that unsigned integers wrap around when you reach their limits. A common mistake is to decrement an index that is 0 already, causing a wrap-around to the maximum value. You saw this in the lesson about for-loops, but let's repeat.

This is an infinite loop, producing undefined behavior once i wraps around. There are two issues here. If `myArray` is empty, ie. size() returns 0 (which is possible with std::array), myArray.size() - 1 wraps around. The other issue occurs no matter how many elements there are. i >= 0 is always true, because unsigned integers cannot be less than 0.

A working reverse for-loop for unsigned integers takes an odd shape:

Suddenly we decrement the index in the condition, and we use the postfix -- operator. The condition runs before every iteration, including the first. In the first iteration, i is myArray.size() - 1, because i was decremented in the condition. When i is 0 and about to wrap around, the condition is no longer true and the loop stops. i actually wraps around when we do i-- for the last time, but it's not used afterwards.

Array of struct

Of course std::array isn't limited to numbers as elements. Every type that can be used in a regular array can be used in a std::array.


House number 13 has 120 rooms
House number 14 has 30 rooms
House number 15 has 120 rooms

However, things get a little weird when we try to initialize the array.

Although we can initialize std::array like this if its elements are simple types, like int or std::string, it doesn't work with types that need multiple values to be created. Let's have a look at why this is the case.

std::array is an aggregate type, just like House. There is no special function for the creation of a std::array. Rather, its internal array gets initialized as if it were a member of a struct. To make this easier to understand, we'll implement a simple array type ourselves.

As of now, we can't do this without having to access the value member. You'll learn how to get around that later. This doesn't affect the issue we're observing.

As expected, this works. So does std::array if we use it with int elements. When we instantiate a struct, we can initialize all of its members. If we try to create an Array of Houses, we get an error.

When we use braces inside of the initialization, the compiler will try to initialize one member of the struct for each pair of braces. Rather than initializing the Array like this:

The compiler tries to initialize the Array like this:

The first pair of inner braces initializes value, because value is the first member of Array. Without the other two pairs of braces, there would be one house with number 13, 4 stories, and 30 rooms per story.

A reminder

Braces can be omitted during aggregate initialization:

To initialize all houses, we need to do so in the first pair of braces.

This works, but it's very confusing. So confusing that your compiler might even warn you about it. If we add braces around each element of the array, the initialization is a lot easy to read.

This is why you'll see an extra pair of braces in initializations of std::array.


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, unless you also omit the type, which isn't always possible), 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
6.14 -- Pointers to pointers and dynamic multidimensional arrays

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

  • Timo

    Hi Alex!
    First I would like to thank you very much! is such a great and complete ressource for learning the C++ language! :-)

    Now to my question:
    I'm curious why you are using the size_type type in your code examples.
    As you already mentioned, this reduces readability much.
    For GCC 7.3 this is just a typedef of std::size_t.
    Also at [1] and [2] it is mentioned that it's only a typedef for std::size_t.
    However I didn't find in the C++ Standard where this is explicitely defined.
    Still, I think this chapter makes beginners insecure if they should really use std::array or better stick to C-arrays.
    Even if size_t will ever differ from size_type the compiler will warn anyways - as it is already doing for int counters.

    What do you think about updating this chapter accordingly?



    • Alex

      size_type is almost always defined as std::size_t, but it's not guaranteed. The standard library uses size_type, so to be most correct, so should you. If you want to do something else (e.g. use size_type, or even unsigned int), you can certainly do so.

      I agree this has readability challenges. This is a well known C++ issue, and I'm hoping they'll address it in a future version of C++.

      • > size_type is almost always defined as std::size_t, but it's not guaranteed
        N4791 §

        The fact that they use an alias for the size type could indicate that @size_type may or may not be used as an alias for a different type in the future.
        Using @std::size_t shouldn't cause problems, but @std::array::size_type is on the safer side.

  • saj

    Instead of creating counter variable 'i' of type 'size_type', can't we typecast .size() into int? just like this:

    • Alex

      You can, but there are two issues:
      1) You risk overflow if the array is really huge (not likely)
      2) std::array::operator[] expects a size_type parameter, so you'll end up implicitly converting i back to a size_type every time you do an array access. Unsure whether this will have any kind of performance impact.

  • I want to know how compiler knows the size of the array (std::array) even when it is passed by reference to a function.

  • Louis Cloete

    @Alex, in your summary, first sentence, you have a typo: "build-in arrays..." should be "built-in arrays..."

  • Jon

    Hello, for this little code snippet example from above:

    is there a reason why you used a reference to the element in the for-each loop? I was under the impression that we should only do that for non-fundamental data types?


  • Rico

    Hi all,

    I am looking to assign a value to a std::array in a function. But the compiler says it is read-only. Anyone can help? Here is my code

  • Hexx

    what if I wanted to create a 2D array using std::array, how do I accomplish this?

  • Jared

    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).

    Just to clarify not that I forgot something but the compiler handles the a const reference and a normal reference alike when we talk about performance right?

  • boteomap2

    Is it possible to dynamically allocating array using std::array?

  • Sagun

    Yo I'm here for a quick look back.
    Does an std::array not decay into a pointer when you pass it, because it is a class?

  • i want to understand something, i tried this i got the length right (i'am not really sure if its has any flaws or not), Is there a way where using std::array that i could not specify the number of elements in the function i'am passing this array to, so i won't have to change in that function every time i add a new element ?


  • tried this:

    but it won't compile and I just get a string of errors such as: C2039 array: is not a member of std.

    • Hi Nigel!

      There are a bunch of errors, but non of which should cause C2039. Try disabling precompiled headers.
      An early hint: @std::find returns an iterator. Although you could use a pointer to store it, I suggest you to use @auto, because iterators tend to have long names.

      • unfortunately, when I remove pch.h it comes up with "unexpected end of file - did you forget to include pch.h?

        • You need to disable precompiled headers in the project settings as well as removing the include.

          • Found this one works:

            If I put std::sort into a separate function with a reference to const std::array it produces further errors and next cannot be assigned to a const variable..  C++ has some seriously weird syntax....

            • > If I put std::sort into a separate function with a reference to const std::array
              The array you're passing is const, but @std::sort needs to modify the array. That doesn't work. You need to pass by non-const reference.

              • Ha got it:

                and it was there all the time.  Thank you for your patience.

                • Was this also the cause for C2039, or was that fixed by removing precompiled headers?


                  * Line 14, 41: Use either @std::array::begin or @std::begin. Without looking it up, I'd say @std::array::begin is faster. The only place @std::begin is useful for are C-style arrays afaik. Same for end.
                  * You're checking for extraction failures and clearing the buffer, but you're not asking for input again.

          • interestingly, after disabling precompiled headers in the new VS2017 update to version 15.8, #include "stdafx.h" can not be opened.  Perhaps it is no longer a requirement with Windows10.

  • Hi Alex,

    Visual Studio has just updated and now instead of #include "stdafx.h" it has #include "pch.h" as its predefined headers should I still add stdafx.h?

  • Winston Lai

    Hi Alex,
    You mentioned in this chapter that:

    "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)."

    but in 6.2 you mentioned that

    "However, because copying large arrays can be very expensive, C++ does not copy an array when an array is passed into a function. Instead, the actual array is passed. This has the side effect of allowing functions to directly change the value of array elements!"

    So does the array get copied when passing into the function? Thanks a lot!

    • Hi Winston!

      "array" in 6.2 means C-style array.
      "array" in 6.15 means @std::array.

      @std::array servers the same purpose as C-style arrays, but their implementations are entirely different. @std::array doesn't follow the rules of C-style arrays, and the other way around.

      Passing a C-Style array passes the pointer.
      Passing an @std::array passes a copy of the entire array.

      • Winston Lai

        Thank you!!. So is it right to say that the C-style array passes a copy of the pointer into the function(pass by address) and that's why the length of the array is unknown to the function, but we can still access the element of of the array with indexing within the function. In std::array, since we are passing the entire copy of the array into the function, therefore, the function knows the length of the array and we can take size() of the std::array within the function?

      • Winston Lai

        Got it! Thank you so much!!

  • Idda

    hi Alex,
    In this chapter you said std::array doesn't get decayed to pointers is this because when they are passed we have to specify the the lenght of the std::array like

    here I had specified the array length!....or its a misunderstanding...

  • Joe

    Hi guys,

    I am having a bit of an issue.
    I am attempting to pass a structured array to a void function to allow modification of its elements and I have run into an
    error I do not understand.

    When I hover over the issue it displays
    a reference of type "std::array<testStructure, 6U> &" (not const-qualified) cannot be inititalized with a value
    of type "std::array<testStructure, 6U>"

    my code is

    • Joe

      If you want to whole code I can put that in here, but its quite large.

      The confusion for myself is coming from my using the same techniques used when writing the black jack game.  
      I placed as a parameter in the function call the Deck and set as parameter at the function the same as my code above.  
      so why it is not working this time around is beyond me.

    • nascardriver

      Hi Joe!

      I don't see anything wrong with your code, try setting up a minimal example that still causes the error, you might find the problem while doing so, if you don't, post the resulting code.
      Also, don't rely on the error messages your IDE shows you while hovering, they aren't always correct or precise. Look at the build log instead (Usually located somewhere at the bottom).

  • Shashank Barthwal

    When I am printing the sorted array after sorting the array through std::sort(), it is showing some strange results- some extra numbers are getting printed on screen.

  • Matt

    Wrote the following to review some older material and practice some of what was covered in this lesson.  I peaked ahead at what we're doing in this chapter's comprehensive quiz and I'm pretty excited.

    No error checking in this code.

    • nascardriver

      Hi Matt!

      Good to see you practicing on your own. You understood everything correctly, nothing wrong with your code, just some notes:

  • ASP

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

    Whats the difference between initialization list and uniform initialization actually?

    • Alex

      The top form is a copy initialization, whereas the bottom form is a direct initialization. They behave similarly in most cases, with only subtle differences between them (such as the latter should work with explicit constructors whereas the former will not).

      In general, the uniform initialization should be preferred.

  • Saumitra Kulkarni

    We pass myarray in main to printlength which has the data type of array and it doesn't decay into pointer.

    But in the argument of printlength

    Why do we have to use the (&) operator for myArray, shouldn't it have the same type as in the argument of myarray that we passed in main.

    And when I tried it in my compiler avoiding the (&) operator it worked just fine.

    • nascardriver

      Hi Saumitra!

      It seems like Alex got ahead of himself. Passing arguments by reference isn't covered until Lesson 7.3.
      The short version of using a reference here is, it's faster. You can write your code without the & and it will work just fine.

  • Marion

    Very short comment, shouldn't you call your variables myArray instead of myarray? (to respect the naming conventions you detailed in 1.4c)

  • Aaron

    Hi Alex,
    Is there a way to define a member variable of type std::array in a base class, then in subclass use a different size of std::array?
    class A
    std::array<int, ?> arr;

    class B: public A
    std::array<int, const_size_B> arr;

    class C: public A
    std::array<int, const_size_C> arr;

    I know size should be known at compile time, but const_size_B and const_size_C can be set to constant for the subclass itself. Is there a way to implement it? Thanks.

    • nascardriver

      Hi Aaron!

      This can be done with templates (Chapter 13).
      An easier approach would be using std::vector instead of std::array (Lesson 6.16).

      PS: Please use CODE tags

  • Nitin Puranik

    Hi Alex,

    First of all I'm extremely grateful for your work here. This is my go-to C++ resource on the web.

    Regarding std::arrays, the huge disadvantage I see is that when you pass it to a function, you need to put down the hardcoded size of the array in the function definition.

    void Foo (std::array<int, 5 /* I'm referring to this '5' */> &myarray) {}

    This would be good only if you know beforehand that you will never use Foo() for any array that isn't of size 5. Pretty limiting if you ask me. Don't you agree? This is where C-style arrays get an upperhand. At least I can declare a generic enough function like

    void Foo (void *myarray, int sizeofarray) {}

    This Foo() can now deal with arrays of any size.

    • Alex

      This is a bit of an immediate downside, but one that has a fairly straightforward solution. If you want a function to be able to work with std::array of any size, you can make that function into a template function.

      Here's an example that you can compile:

      I talk about templates in chapter 13.

      The above example will actually work with any type that you can iterate over using a for-each loop (not just std::array), so you could also pass in a std::vector if you wanted.

      If you want to restrict the function to just std::array, you use the following prototype for function print instead of the one above:

      This will allow the function to be called with std::array of any type and size, but not other types.

  • 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?


    • 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

        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;

      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 all code inside code tags: [code]your code here[/code]