16

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?

Community
  • 1
  • 1
Zereges
  • 5,139
  • 1
  • 25
  • 49
  • Possible duplicate of [Passing function objects into std algorithms by reference](https://stackoverflow.com/questions/34825552/passing-function-objects-into-std-algorithms-by-reference) – underscore_d Sep 18 '18 at 19:02

1 Answers1

14

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&& );
Barry
  • 286,269
  • 29
  • 621
  • 977