Plast interview

32. Move vs Copy

In a matching engine, message objects carry strings and vectors that trigger heap allocations. Under nanosecond budgets, avoiding unnecessary copies and allocator churn is critical.

struct Msg { std::string sym; std::vector<int> lots; };
void publish(std::vector<Msg>& out, Msg m){ out.push_back(std::move(m)); }
int main(){
  Msg m{"AAPL",{1,2,3}}; std::vector<Msg> q;
  publish(q, m);
  publish(q, std::move(m));
}

Part 1.

Describe the observable effects on m after each call and count Msg-level copies/moves into q. Propose a more efficient publish signature for rvalues.

Part 2.

(1) When is pass-by-value plus moving preferable to T&& in hot paths?

(2) How does const on parameters inhibit moves and affect performance?

(3) Why should Msg's move operations be noexcept for vector<Msg> growth?

(4) When does emplace_back outperform push_back for move-enabled types?

(5) What are the valid postconditions of a moved-from Msg?

Answer

Answer (Part 1)

First call publish(q, m) performs one copy to the by-value parameter, then one move into q; original m remains unchanged. Second call publish(q, std::move(m)) performs two moves; m is valid but unspecified (typically empty sym/lots).

Prefer constructing directly in-place from rvalues: void publish(std::vector<Msg>& out, Msg&& m) noexcept { out.push_back(std::move(m)); } or a forwarding form: template<class... A> void publish(std::vector<Msg>& out, A&&... a){ out.emplace_back(std::forward<A>(a)...); }.

Answer (Part 2)

(1) When you must make an owning copy internally anyway. Otherwise T&& avoids an extra move for rvalues.

(2) std::move on const T yields const T&&, selecting copy. Const blocks moving assignments/constructors, increasing copies.

(3) Without noexcept, vector may copy on reallocation. With noexcept, it moves elements, reducing latency and allocations.

(4) When constructing from arguments, avoiding a temporary. For T&&, push_back(std::move(t)) and emplace_back are typically equivalent.

(5) It remains valid but unspecified. You may destroy, assign, or reinitialize; only invariants like empty() may hold.