Fixed-Size Arrays with std::array
Use the safer, fixed-size array wrapper from the standard library.
What Is std::array?
std::array is a fixed-size array container in the C++ standard library that wraps a C-style array in an object with a safer, more convenient interface. It is the preferred choice when the array size is known at compile time.
While std::vector excels at runtime-sized collections, its limited constexpr support restricts compile-time optimizations. When you need arrays evaluated at compile time, std::array is the solution.
When fixed-size arrays make sense
Dynamic arrays like std::vector provide flexibility but involve trade-offs:
- Slight performance overhead compared to fixed-size arrays
- Minimal constexpr support (limited to trivial uses)
Modern C++ emphasizes constexpr for safer, more optimizable code. When your array size is known at compile time and you want compile-time evaluation, std::array delivers.
Use `std::array` for constexpr arrays with known sizes. Use `std::vector` for runtime-sized or non-constexpr arrays.
Declaring std::array
std::array resides in the <array> header. Unlike std::vector, it requires the size as a template argument:
#include <array>
#include <vector>
int main()
{
std::array<int, 4> slots{}; // std::array of 4 ints
std::vector<int> items(4); // std::vector of 4 ints (comparison)
return 0;
}
std::array requires two template arguments:
- Element type (
int) - Array length (
4) - a non-type template parameter
The length must be a constant expression
Unlike std::vector which accepts runtime sizes, std::array demands a constant expression for length:
#include <array>
int main()
{
std::array<int, 6> a{}; // Literal constant
constexpr int capacity{ 10 };
std::array<int, capacity> b{}; // Constexpr variable
enum Weapons { sword, bow, staff, max_weapons };
std::array<int, max_weapons> c{}; // Enumerator
return 0;
}
Non-const or runtime values won't compile:
#include <array>
int main()
{
int slots{ 8 };
std::array<int, slots> d{}; // Error: slots is not constexpr
return 0;
}
`std::array` permits zero length, creating an object with no data. Accessing elements of such arrays causes undefined behavior. Use `empty()` to check for this condition.
Aggregate initialization
std::array is an aggregate (it lacks constructors), so it uses aggregate initialization:
#include <array>
int main()
{
std::array<int, 4> levels{ 1, 5, 12, 25 }; // List initialization (preferred)
std::array<int, 3> ranks = { 100, 250, 500 }; // Copy-list initialization
return 0;
}
Elements initialize sequentially from index 0.
Without initializers, elements are default-initialized (often leaving them uninitialized for fundamental types). Always use empty braces for value-initialization:
#include <array>
int main()
{
std::array<int, 4> uninitialized; // Elements may contain garbage
std::array<int, 4> zeroed{}; // All elements are zero (preferred)
return 0;
}
Too many initializers cause compilation errors; too few value-initialize remaining elements:
#include <array>
int main()
{
std::array<int, 2> a{ 10, 20, 30 }; // Error: too many initializers
std::array<int, 5> b{ 10, 20 }; // OK: b[2], b[3], b[4] are zero
return 0;
}
Const and constexpr std::array
std::array can be const:
#include <array>
int main()
{
const std::array<int, 3> thresholds{ 100, 500, 1000 };
// Elements are implicitly const
return 0;
}
More importantly, std::array supports constexpr fully:
#include <array>
int main()
{
constexpr std::array<int, 5> fibonacci{ 1, 1, 2, 3, 5 };
// Evaluated at compile time!
return 0;
}
This constexpr capability is std::array's defining advantage.
Define `std::array` as constexpr whenever possible. If constexpr isn't needed, consider `std::vector` for its flexibility.
Class template argument deduction (C++17)
CTAD allows the compiler to deduce both type and length:
#include <array>
int main()
{
constexpr std::array scores{ 85, 92, 78 }; // Deduces std::array<int, 3>
constexpr std::array weights{ 1.5, 2.3, 0.8 }; // Deduces std::array<double, 3>
return 0;
}
Use CTAD to let the compiler deduce `std::array` type and length from initializers.
CTAD doesn't support partial specification (as of C++23):
#include <array>
int main()
{
std::array<int> a{ 1, 2, 3 }; // Error: length missing
std::array<3> b{ 1, 2, 3 }; // Error: type missing
return 0;
}
Deducing length with std::to_array (C++20)
std::to_array enables length deduction when you need to specify only the type:
#include <array>
int main()
{
constexpr auto arr1{ std::to_array<int, 4>({ 5, 10, 15, 20 }) }; // Both specified
constexpr auto arr2{ std::to_array<int>({ 5, 10, 15, 20 }) }; // Type only
constexpr auto arr3{ std::to_array({ 5, 10, 15, 20 }) }; // Deduce both
return 0;
}
However, std::to_array creates a temporary then copies it, making it more expensive than direct construction. Use it only when CTAD can't determine the type:
#include <array>
int main()
{
// No literal for short, so specify the type explicitly
constexpr auto levels{ std::to_array<short>({ 1, 2, 3, 4 }) };
return 0;
}
Accessing elements
Use operator[] just like std::vector:
#include <array>
#include <iostream>
int main()
{
constexpr std::array<int, 5> ranks{ 10, 25, 50, 100, 200 };
std::cout << ranks[2] << '\n'; // Prints 50
std::cout << ranks[8] << '\n'; // Undefined behavior: out of bounds
return 0;
}
As with other containers, operator[] performs no bounds checking. Invalid indices cause undefined behavior.
Summary
std::array vs std::vector: std::array is a fixed-size array with complete constexpr support, ideal for compile-time arrays. std::vector is dynamic but has minimal constexpr support, making it unsuitable for compile-time operations.
Non-type template parameters: std::array requires two template arguments: element type and array length. The length is a non-type template parameter of type std::size_t that must be a constant expression.
Aggregate initialization: std::array is an aggregate (no constructors), using aggregate initialization. Elements initialize sequentially from index 0. Without initializers, elements are default-initialized; use empty braces for value-initialization.
Constexpr capabilities: std::array has complete constexpr support, enabling compile-time array creation and manipulation. This is its primary advantage over std::vector.
CTAD support (C++17+): Class Template Argument Deduction lets the compiler deduce both element type and array length from initializers, eliminating explicit template arguments.
std::to_array (C++20): Allows length deduction when specifying only the element type. However, it creates and copies a temporary, making it costlier than direct construction. Use only when CTAD can't determine the type.
Zero-length arrays: std::array permits zero length, creating an object with no data. Accessing elements causes undefined behavior. Use empty() to check for this condition.
Choose std::array for constexpr arrays with known, fixed sizes. Otherwise, prefer std::vector for flexibility and move semantics.
How did you find this lesson?
Your rating helps us improve the content.
Create an account to track your progress and access interactive exercises. Already have one? Sign in.
Fixed-Size Arrays with std::array - Quiz
Test your understanding of the lesson.
Practice Exercises
Introduction to std::array
Practice using std::array for fixed-size arrays. Learn initialization, element access, and common operations.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!