13. Span Optional Variant
In a bursty market-data decoder, zero-copy parsing and predictable latency are critical. We want to consume a read-only level array without copying, opportunistically updating a preallocated sink if present. Modern utility types like std::span, std::optional, and std::variant can express this without sacrificing performance.
struct Book { int best; };
std::optional<int> upd(std::span<const int> lvls, std::variant<Book*, std::monostate> dst) {
if (lvls.empty()) return std::nullopt;
int b = lvls.front();
if (auto p = std::get_if<Book*>(&dst)) if (*p) (*p)->best = b;
return b;
}
Part 1.
Does this achieve zero-copy and safe updates under feed bursts? Identify lifetime and exception-safety pitfalls and propose minimal API changes to harden it for a hot path.
Part 2.
(1) When does std::span become dangling in typical feed-handler pipelines?
(2) std::optional<int> vs sentinel -1: branch prediction and codegen implications?
(3) Pass std::variant<Book*, std::monostate> by value or reference; why?
(4) What const-correctness applies to std::span<const int> and Book* here?
(5) Should upd be noexcept or constexpr? Why?
Answer
Answer (Part 1)
It is zero-copy if the caller-owned buffer outlives the call; std::span only aliases existing memory. Harden by requiring a stable owner (e.g., ring-buffer slot not overwritten during the call), making upd noexcept, and passing const std::variant<Book*, std::monostate>& to avoid variant copies; also null-check the pointer or prefer a Book& alternative to forbid nulls.
Answer (Part 2)
(1) When the owner of the referenced storage ends or reuses the memory (e.g., temporary vector, recycled ring slot). A std::span to such memory becomes immediately dangling.
(2) std::optional encodes absence explicitly and can compile to a branch or flag register. A sentinel can be branchless but risks conflating valid data with the sentinel.
(3) By-value copies the discriminant and pointer, potentially increasing register pressure. A const& avoids copies and improves cache friendliness; mutate-only APIs can take &.
(4) std::span<const int> guarantees the function won't mutate levels via this view. The Book* should be non-const only if mutation is intended; otherwise use const Book*.
(5) noexcept enables better inlining and elides unwind machinery; failures are signaled via std::optional. constexpr is not meaningful here due to runtime side effects; leave it non-constexpr.