4

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.

user8063157
  • 285
  • 1
  • 13
  • 2
    The default behavior is safer. Say I wanted to call `process` twice in `logAndProcess`. If you leave out the `std::forward`, `param` will be copied. Instead, if the behavior was what you want it to be, then the second call to `process` would have a `param` that may have been moved from by the first call to `process`. Also, *universal references* are now known as *forwarding references*. – Praetorian Aug 24 '17 at 14:21
  • @Praetorian , so if you use std::forward are you sure if second use is safe? I mean even by using std::forward you dont know what has happened to the param , whether it was moved or copied as you dont know how the function was called, by lvalue or rvalue . – user8063157 Aug 24 '17 at 14:29
  • @user8063157 - No. But if rvalues were always moved by default, you could only use them once and then they were gone. Now you have to explicitly mark your last use with `std::move` or `std::forward` to let them "move on". – Bo Persson Aug 24 '17 at 14:33
  • @Bopersson , will last use with std::forward won't do injustice to the In between calls , which will do copy? Having said that compiler can also detect when forwarding reference is last used and do correct thing ! – user8063157 Aug 24 '17 at 14:51
  • 1
    And what is the correct thing? Maybe in the example I presented, I want the first call to `process` to conditionally move from `param`, and I have some way of detecting that in the second call and behave accordingly. So copying in the first case and forwarding in the second is not what I want. Compilers cannot read the programmer's mind, so the sane default is to behave in a manner that's safe in all cases unless told otherwise. – Praetorian Aug 24 '17 at 16:00
  • [Can compiler generate std::move for a last use of lvalue automatically?](https://stackoverflow.com/questions/15387271/can-compiler-generate-stdmove-for-a-last-use-of-lvalue-automatically) – cpplearner Aug 24 '17 at 20:06

1 Answers1

1

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
songyuanyao
  • 169,198
  • 16
  • 310
  • 405