2. Compile-time costs
In nanosecond trading systems, compile-time constants avoid runtime branches and dynamic allocation, reducing jitter. Feed handlers often build lookup tables and fixed rings whose sizes must be known at compile time. Misusing const versus constexpr can silently force slower runtime paths or non-portable code.
int read() noexcept;
constexpr int N = 64;
const int M = read();
int a[N]{};
int b[M]{};
constexpr int D = N + 1;
static_assert(D > 0);
Part 1.
Which declarations are valid in standard C++, and which are ill-formed? How would you fix the invalid one while preserving predictable latency?
Part 2.
(1) What guarantees does constexpr give that const does not, regarding N and M?
(2) When is const int M = read(); a constant expression?
(3) How can constexpr functions reduce hot-path latency for LUTs?
(4) If using std::vector<int> b(M), implications for allocation and predictability?
(5) std::array<int, N> vs int a[N]: layout, ABI, and optimization differences?
Answer
Answer (Part 1)
N, a[N], D, and static_assert are valid; M is run-time const, so int b[M]{} is ill-formed (no VLAs in standard C++). Fix by making M a constexpr, or use dynamic storage like std::vector<int> b; b.resize(M); or std::unique_ptr<int[]> p(new int[M]);, ideally backed by a preallocated arena to avoid heap jitter.
Answer (Part 2)
(1) constexpr enforces compile-time evaluation and implies const, enabling array bounds and template arguments. const may be run-time initialized, forbidding those contexts.
(2) Only when its initializer is a constant expression. Because read() isn’t constexpr, M isn’t a constant expression.
(3) They precompute tables and branchless values at compile time, removing hot-path work. This reduces jitter and improves instruction-cache predictability.
(4) It allocates dynamically (often heap) and adds indirection; growth can introduce unpredictable pauses. Prefer preallocation, pools/arenas, or fixed constexpr bounds.
(5) std::array<int, N> is zero-overhead; layout matches int[N] for trivial types and is often ABI-identical. It adds safer APIs and type semantics without runtime cost.