2
votes

I'd appreciate your help understanding std::forward implementation.

What's the difference between std::forward and a standard cast to T&& in a template function? In this example at least, they both seem to do the forwarding work correctly. The LValue gets forwarded to the LValue overload, and the RValue is forwarded to the RValue overload.

struct DemoPerfectForwardVsRefRefCast
{
    void overloaded(int const &arg) { std::cout << "receive by lvalue\n"; }
    void overloaded(int && arg) { std::cout << "receive by rvalue\n"; }

    template< typename t >
    void forwarding(t && arg) {
        std::cout << "via std::forward: ";
        overloaded(std::forward< t >(arg));  // forwards correctly
        std::cout << "via && cast: ";
        overloaded(static_cast<t&&>(arg)); // seems to also forward correctly
    }

    void demo()
    {
        std::cout << "initial caller passes rvalue:\n";
        forwarding(5);
        std::cout << "initial caller passes lvalue:\n";
        int x = 5;
        forwarding(x);
    }

};

Is universal reference + reference collapsing + static cast to T&& enough?

In Visual Studio 2017 the definition of std::forward is implemented differently, with two template functions, one with a T& param, and one with a T&& param. Is that necessary? Is it better than just the cast?

1
eli.thegreenplace.net/2014/… also features the two template functions implementation - Daniel Jour
More importantly, the standard defines the return value of std::forward<T>(x) as static_cast<T&&>(x). - HTNW

1 Answers

1
votes

In summary, in order just to forward a variable you could use a simple cast. The reason a function was introduced was primarily to make the code cleaner.

However, there is a slight advantage beyond that. In the vast majority of the times, the l-value variant of std::forward will be called, as every named variable is an l-value. But in some special cases, you might want to forward a function-call result (such as std::move). In this case, the casting static_cast<T&&>(std::move(t)) will not work if t is an lvalue, because casting from r-value to l-value is prohibited.

I believe that the second use-case is extremely rare, however it is a possibility that should be covered.