Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why or when should I cast callable function parameters to an rvalue before invocation?

So far, I have considered passing and invoking a callable as

template <class Fct, class... Args> void f(Fct&& g, Args&&... args)
{
    g(std::forward<Args>(args)...);
}

is the way to go. Now in this talk (at 34 min.) and in the std::invoke example implementation, I saw an equivalence of the above snippet that conditionally casts the callable to an rvalue reference before invoking it,

template <class Fct, class... Args> void f(Fct&& g, Args&&... args)
{
    std::forward<Fct>(g)(std::forward<Args>(args)...);
}

I assume that this modification only affects closures, but I still don't understand why the second version is preferable: the cast only affects rvalue arguments and no state should be copied upon invocation, correct? I also checked whether std::function::operator() is overloaded on & and && to get a hint by a library alternative to the above snippets, but this is not the case.

Thanks in advance for hints and answers!

like image 377
lubgr Avatar asked May 22 '26 23:05

lubgr


1 Answers

The point of perfect forwarding is to preserve the original information as much as possible.

g(std::forward<Args>(args)...); will drop the rvalue/lvalue information of the original function object, g will always be treated as lvalue.

This will cause observable effect, for example:

struct foo {
    void operator()(int) & {
        std::cout << "& called\n";
    }

    void operator()(int) && {
        std::cout << "&& called\n";
    }
};

foo{}(1) will invoke the second operator(). If you use your first approach without std::forward, f(foo{}, 1) will invoke the first operator().

like image 116
llllllllll Avatar answered May 25 '26 13:05

llllllllll



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!