Plast interview

7. Move or Copy

In a packet gateway, unnecessary copies of large messages amplify heap churn and cache misses, inflating tail latency. Choosing move-friendly APIs while preserving correctness is critical. Assess the snippet to minimize allocations and maximize predictability.

struct Chunk { std::unique_ptr<int[]> buf; };
Chunk make(); void sink(Chunk); void view(const Chunk&);
int main(){
  Chunk c = make();
  sink(c);
  sink(std::move(c));
  view(c);
}

Part 1.

Which calls compile, and which perform copy, move, or elision? Describe c's state after the move and propose the minimal API change, if any.

Part 2.

(1) When is copy elision in Chunk c = make() guaranteed, and how does it affect latency?

(2) Should Chunk's move constructor be noexcept? Explain consequences for containers and generic algorithms.

(3) Prefer void sink(Chunk) vs void sink(Chunk&&) vs void sink(const Chunk&) in hot paths? Trade-offs.

(4) After moving from c, what invariants must Chunk maintain to be safely inspectable by view?

(5) How do pass-by-value parameters interact with deleted copy constructors and overload resolution?

Answer

Answer (Part 1)

sink(c) does not compile: passing an lvalue to a by-value parameter requires a copy, but Chunk's copy is deleted due to unique_ptr. Chunk c = make() performs guaranteed copy elision in C++17+, constructing c directly; sink(std::move(c)) move-constructs the parameter; view(c) is fine.

After the move, c remains valid but is in a moved-from state; for unique_ptr, c.buf is null. Minimal API change: make consumption explicit, e.g., accept Chunk&& (or keep by-value and require callers to std::move), and keep observers as const Chunk&.

Answer (Part 2)

(1) Since C++17, returning a prvalue into a named object is guaranteed elided. It removes a move, reducing allocations and cache misses.

(2) A noexcept move lets containers move during reallocation; otherwise they may copy or reject operations. With no copy, non-noexcept move can disable growth.

(3) By-value is simple and forces explicit std::move, but may add an extra move internally. Chunk&& signals consumption and can avoid extra moves; const& is for observing.

(4) Moved-from objects must be valid but unspecified. Ensure invariants like buf == nullptr and methods tolerate empty state.

(5) For lvalues, by-value selects the copy constructor; if deleted, it’s ill-formed. Rvalues select the move constructor; provide && or enforce std::move.