Plast interview

24. Perfect Forwarding

In a matching engine adapter, we must build tiny message objects without accidental copies or pessimizing moves. Perfect forwarding and reference collapsing determine whether we propagate lvalue/rvalue and const correctly, directly impacting latency and codegen. Analyze the template below.

struct Msg{int a; explicit Msg(int v):a(v){}};
template<class T, class... Args>
T make(Args&&... args){
  return T(std::forward<Args>(args)...);
}
int main(){
  auto m = make<Msg>(1);
}

Part 1.

Explain what Args deduces to in the call shown, and why std::forward is required. If calling int x=1; make<Msg>(x);, what collapses and which constructor invocation occurs?

Part 2.

(1) When should std::forward be used versus std::move inside make?

(2) How does const on lvalue arguments change Args deduction and call behavior?

(3) What happens when passing braced initializers; can Args deduce std::initializer_list here?

(4) Should make be noexcept(std::is_nothrow_constructible_v<T, Args...>)? Impact on codegen and latency?

(5) Explain reference collapsing with overloads: T&, T&&, const T& targets of forwarded arguments.

Answer

Answer (Part 1)

make<Msg>(1) deduces Args = {int}, so parameters are int&&; std::forward<int> yields an rvalue, invoking Msg(int) directly without intermediate copies. With int x=1; make<Msg>(x);, Args = {int&} and reference collapsing yields an int&; std::forward<int&> returns an lvalue and still calls Msg(int) by passing the lvalue’s value.

Answer (Part 2)

(1) Use std::forward to preserve callers’ value categories. std::move wrongly turns lvalues into rvalues, breaking semantics and overload selection.

(2) A const lvalue deduces as const T&; forwarding returns const T&. Moving is disabled; only copy/const-qualified overloads participate.

(3) Braced lists don’t deduce Args automatically. You need an overload taking std::initializer_list<U> or pass an explicit std::initializer_list.

(4) Yes. Propagating noexcept enables better inlining, shrink-wrapping, and move-if-noexcept choices, reducing unwinding edges and tail latency.

(5) Forwarding preserves categories: lvalues map to T& overloads, rvalues to T&&, and const lvalues to const T& overloads.