Plast interview

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.