Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perfect forwarding a functor

I wonder what's the right way of using a perfect forwarded functor? Here's two code snippet. Which one is the best, and if neither, what is the best form?

template<typename T, typename... Args>
void callMe(T&& func, Args&&... args) {
    func(std::forward<Args>(args)...);
}

Or

template<typename T, typename... Args>
void callMe(T&& func, Args&&... args) {
    std::forward<T>(func)(std::forward<Args>(args)...);
}

EDIT:

Will it impact overload resolution? If func's operator() has ref-qualifier for && or const &, should I do the latter version and should I care about which overload I call?

Thanks!

like image 374
Guillaume Racicot Avatar asked Feb 11 '16 20:02

Guillaume Racicot


2 Answers

Since ref-qualified operator() exists, the first version could do the wrong thing. Consider:

struct C {
    void operator()() && { std::cout << "rval\n"; }
    void operator()() const & { std::cout << "lval\n"; }
};

callMe(C{});

I'm giving you an rvalue - and would expect to see "rval" - but in the first version, you're always treating the function object like an lvalue - so I really see "lval".

So the correct solution would be the second - which forwards func as well.


In practice, I don't know how often ref-qualified member functions actually happen, so the former is likely fine.

like image 181
Barry Avatar answered Sep 21 '22 21:09

Barry


Perfect forwarding is useful in the case where you are going to deliver an object (possibly an rvalue) to some other function, and you're not sure if it's an rvalue or an lvalue. In this case, you just use func, so there's no gain or harm in forwarding it.

Remember, std::forward is a conditional std::move, which is only a cast to an r-value reference.

In addition, it's really only useful if there's a possibility that a copy or move constructor might be called for the thing you forward. In many cases, const T& will do just fine.

edit:

As berry points out, it does matter if func has ref-qualified operator(). I've never seen or used a ref qualified method (as far as I know. It's extremely rare.) More in Barry's answer.

It'd read this also: What is "rvalue reference for *this"?

struct C {
    void operator()() && { std::cout << "rval\n"; }
    void operator()() const & { std::cout << "lval\n"; }
};

callMe(C{});
like image 28
Johan Lundberg Avatar answered Sep 23 '22 21:09

Johan Lundberg