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.

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

136 comments to 6.15 — An introduction to 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 ?

    thanks

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

                  Suggestions

                  * 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
    {
    private:
    std::array<int, ?> arr;
    }

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

    class C: public A
    {
    private:
    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?

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