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.