short

std::array became compile-time in pieces, and the algorithms went last

· english· audience: working-cpp· AI-generated, reviewed by Filip Sajdak

Compile-time containers arrived in instalments, and the order confuses people who assume std::array was always fully usable in a constant expression.

  • C++11 gave std::array a constexpr constructor and constexpr element access, but almost nothing else.
  • C++14 made more member functions constexpr, though non-const operator[] still lagged.
  • C++17 finished the container itself: begin, end, data, fill, swap.
  • C++20 finished the algorithms, which is the step that actually mattered. std::sort, std::iota, the whole <algorithm> and much of <numeric> became constexpr, along with the ranges versions.

That last step is the one worth internalising. Between C++17 and C++20 you could build a std::array at compile time but not sort it with the standard library, which is why so much older lookup-table code contains a hand-written compile-time bubble sort. Those can now be deleted.

[64, 49, 36, 25, 16, 9, 4, 1]
sum = 204

Every step runs during compilation: std::iota fills 1 through 8, the loop squares each element, and std::ranges::sort with std::greater reverses the order. The static_asserts are the proof rather than decoration. If any part of build() escaped to runtime the program would not compile, so a green build is the guarantee.

Where this pays off

The practical use is a table you would otherwise generate with a script or a macro: sorted keyword lists for a lexer, precomputed CRC or sine tables, a perfect-hash seed, dispatch tables sorted for binary search. Written this way the table lives in the source in readable form, is checked by the compiler, and reaches the binary as constant data in .rodata with no static initialiser and no startup cost.

Two limits are worth knowing before you lean on it. Compilers cap constant evaluation (-fconstexpr-ops-limit on GCC, -fconstexpr-steps on Clang), so very large tables can exceed the budget and need the limit raised. And every element you compute this way is compile time you pay on every build, which is a trade you want to make deliberately rather than by habit.

if consteval and the C++26 work on constexpr exceptions keep pushing the boundary outward. The rule of thumb has flipped: instead of asking what you are allowed to do at compile time, assume you can, and check only when the compiler objects.


Sources: cppreference: std::array and constexpr algorithms (P0202) · Jason Turner, C++ Weekly ep 543 “The constexpr Evolution of std::array”.