In discussion we had here I was playing around with passing functors. C++ STL passes functors as values (seen in std::for_each
, std::find_if
, std::transform
)
So declaring mine would be like this.
template<typename F>
void call_me(F f)
{
f();
}
Now, calling call_me(ftor{})
could probably invoke ftor
's copy constructor (it will most likely be copy elided, so not the case). But ftor f{}; call_me(f);
will result in copying. If ftor
has large data, it might be an issue.
We'll improve it by passing it as const reference (void call_me(const F& f)
) to get rid of unneeded copy. This is fine as long as ftor::operator()
is const
. If it is not, the call to call_me
would result in compilation error (losing const
qualifiers).
So, why to bother with const reference, use just reference (void call_me(F& f)
). This is fine, but it would not work for first case being call_me(ftor{})
since binsing r-value to (non-const) l-value reference is not valid.
Declaring f
as forwarding reference (void call_me(F&& f)
) seems to work in all cases. I believe, that this works thanks to reference collapsing.
So, why are templated functors not passed as forwarding references in functions from STL?
A functor (or function object) is a C++ class that acts like a function. Functors are called using the same old function call syntax. To create a functor, we create a object that overloads the operator(). The line, MyFunctor(10); Is same as MyFunctor.
A C++ functor (function object) is a class or struct object that can be called like a function. It overloads the function-call operator () and allows us to use an object like a function.
Function objects (also called functors) are an STL feature that you may not employ immediately when you start using the STL. They are, however, very useful in many situations and an STL facility with which you should become acquainted.
why are templated functors not passed as r-value references in functions from STL?
First of all, they'd be forwarding references, not rvalue references.
That said, as with many "why" questions about language design in general, the answer can simply be: because nobody has proposed this change yet (see how to submit a proposal). I suspect a great deal of function objects passed into the standard library algorithms are either lambdas, stateless, or extremely cheaply copyable. Moreover, if you have such an expensive object you can always turn in it into a cheaply copyable one for the purposes of the algorithm:
call_me(std::ref(huge_object));
Lastly, some of these algorithms rely upon passing these functions objects around to other helpers. If the algorithm simply assumes that the function object is "free" to copy, this makes it easy to code. If we introduce the possibility of rvalues here, this adds another layer of questions that need to be dealt with - do we just pass in the reference around? What about function objects with ref-qualified operator()
?
Some combination of the above probably explains why, e.g., it's still:
template <class InputIt, class UnaryPredicate>
InputIt find_if(InputIt, InputIt, UnaryPredicate );
and not
template <class InputIt, class UnaryPredicate>
InputIt find_if(InputIt, InputIt, UnaryPredicate&& );
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With