When using templates to support functor as arguments, what qualifier should I use?


When using templates to support functor as arguments, what qualifier should I use?



Consider this code:


template<class F>
void foo1(F f) { f(); }

template<class F>
void foo2(F const& f) { f(); }

template<class F>
void foo3(F&& f) { f(); }



Which version of foo should I use? foo1 is what I see most "in the wild" but I fear that it might introduce copies that I don't want. I have a custom functor that is kind of heavy to copy so I would like to avoid that. Currently I'm leaning towards foo3 (as foo2 would disallow mutating functors) but I'm unsure about the implications.


foo


foo1


foo3


foo2



I'm targeting C++11.





foo3 receive a forwarding reference but does not forward. The body should be std::forward<F>(f)(). I'd still suggest foo1 though.
– Guillaume Racicot
Jul 1 at 14:44


foo3


std::forward<F>(f)()


foo1




1 Answer
1



I’d actually prefer foo3 over foo1 (though the body should be std::forward<F>(f)();)


foo3


foo1


std::forward<F>(f)();



foo3 will cause type F to be deduced to the type that lets you perfectly forward the argument due to reference collapsing. This is useful, as you won’t make copies of anything by default, and can maintain the value category (lvalue vs rvalue) if you decide you want to forward the functor to something that does want a copy of it.


foo3


F



In general, the first form (foo1) is fine if your function is going to store its own copy of the functor, but the third form (foo3) is better for forwarding (using) arguments.


foo1


foo3



I also recommend Scott Meyers’ excellent post about universal references, as well as this related Stack Overflow question.





std::forward should not be used here. Nothing in the question is being forwarded. The functor is only being called.
– super
Jul 1 at 15:01


std::forward





Thanks. Updated the answer to be correct. @super, the functor should absolutely be forwarded. It can affect overload resolution. See the answer in the SO question I added a link to.
– John Drouhard
Jul 1 at 15:15





I see. A bit of an unusual scenario, but very valid. Probably a good habit to adapt to ensure code works under all possible scenarios.
– super
Jul 1 at 15:21





@super I'm not sure if you really should forward (unless you never call the parameter more than once). If operator() has no separate &&-qualified overload, then forwarding does nothing anyway. But if there is such overfload (though I've never seen anyone do that) and if the functor was passed as an rvalue and got moved with std::forward, then calling operator() can leave it in an unspecified state, which is not good if you're going to call operator() on it again.
– HolyBlackCat
Jul 1 at 15:25



operator()


&&


std::forward


operator()


operator()





@HolyBlackCat, how would it be moved with std::forward? Would you have an example where this can go wrong?
– Jeff Garrett
Jul 2 at 16:06






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

How to add background colour in existing image using Swift?

Moria Casán

What's the difference between DDS and SOME/IP?