user8063157 user8063157 - 2 months ago 11
C++ Question

Why is std::forward needed, can't the compiler do the correct thing by default

With

std::forward
being a conditional cast, why can't the compiler do the work automatically when it sees the parameter which user is trying to pass to other function came as Universal reference.

Means why compiler will place the onus of doing right thing on user by way of writing
std::forward
.

Taking example from Effective modern C++.

void process(const Widget& lvalArg); // process lvalues
void process(Widget&& rvalArg); // process rvalues

template<typename T> // template that passes
void logAndProcess(T&& param) // param to process
{
auto now = // get current time
std::chrono::system_clock::now();
makeLogEntry("Calling 'process'", now);
process(std::forward<T>(param));
}


In above code sample I know removing
std::forward
will choose the incorrect overload for process , but to do the right thing of choosing correct overload why user need to write
std::forward
, I mean can't compiler do the obvious for us and for user who don't want to do the correct thing , we can have
std::dont_forward
instead.

I might be missing some use case where compiler can be confused to what's correct but in above case where param being universal reference and two overload of process given to compiler I don't see any confusion.

Just to explain how this question is diffrent that its not about why we need 'std::forward' in current compiler behaviour but why cant compiler do the obvious by default that is to call correct overload of function when forwarding reference is passed around , including detection of multiple use and casting to rvalue on last use.

Answer Source

As a named parameter, param is always an lvalue. That means without std::forward, process(param); will always call the lvalue overload.

On the other hand, you need to tell the compiler when you want to convert it to rvalue explicitly; the compiler can't make the decision for you. e.g.

process(param);                  // you don't want param to be passed as rvalue and thus might be moved here
...
process(std::forward<T>(param)); // it's fine to be moved now