8

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!

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141

2 Answers2

7

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    Cool. I had no idea ref-qualification existed. – Johan Lundberg Feb 11 '16 at 21:10
  • The fear I'd have is an optimization of lambda, where rvalue lambdas start implicitly moving from their stored internal state like local variables. This could give you some nice performance boosts in some cases maybe, and lacking the forward would be lost. But probably not. – Yakk - Adam Nevraumont Feb 12 '16 at 16:14
  • @Yakk I read that as "it's probably impossible to correctly write generic code in C++" – Barry Feb 12 '16 at 16:47
5

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{});
Community
  • 1
  • 1
Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
  • +1 agreed. For other readers, I'd like to point out that there's no harm in forwarding it if you're only going to use it because `std::forward` is merely a typecast to r-value reference (`&&`) or l-value reference (`&`). – AndyG Feb 11 '16 at 20:38
  • Disagree on "gain/harm". What if `T` has ref-qualified `operator()`? – Barry Feb 11 '16 at 20:55