c++c++23perfect-forwardingclang-tidycpp-core-guidelines

Should we still always std::forward a universal reference argument even if unnecessary?


Consider the following code:

#include <string>

auto f1(auto&& v) {
    return v;
}

auto f2(auto&& v) {
    return std::forward<decltype(v)>(v);
}

int main() {
    return f1(std::string("hello")).size() + f2(std::string("hello")).size();
}

clang will generate the same assembly code for f1 and f2. However,

Should we still always std::forward a universal reference argument? or the C++ core guidelines should be updated?


Solution

  • You should use std::forward with universal (forwarding) references when perfect forwarding is actually required, typically when passing the parameter to another function or constructor that depends on value category (e.g., distinguishing between lvalue/rvalue overloads or enabling move semantics).

    In your example, both f1 and f2 return the parameter, and auto return type triggers move semantics for rvalues anyway due to return value optimization (RVO), so is not strictly necessary here. However, using it is good practice in generic code because it preserves the value category and avoids surprises if the function is later changed to forward its argument.