Isn't it better to pass function objects into the STL algorithms by forwarding reference rather then by value? It would allow one to utilize ref-qualifiers of operator ()
of function objects passed.
There are a couple of questions about std::for_each
algorithm on SO (1, 2), which are considering a problem with changing of observable state of function object passed to std::for_each
.
Passing functional objects by lvalue leference would solve the problem as a side effect even for those of algorithms, which can't return functional object (due to they should return, say, output iterator last value or something else).
For example the algorithm std::for_each
can be changed from (copied from libc++):
template<typename _InputIterator, typename _Function>
_Function
for_each(_InputIterator __first, _InputIterator __last, _Function __f)
{
for (; __first != __last; ++__first)
__f(*__first);
return _GLIBCXX_MOVE(__f);
}
to:
template<typename _InputIterator, typename _Function>
_Function &&
for_each(_InputIterator __first, _InputIterator __last, _Function && __f)
{
for (; __first != __last; ++__first)
_GLIBCXX_FORWARD(_Function, __f)(*__first);
return _GLIBCXX_FORWARD(_Function, __f);
}
or (if such breaking changing is allowed) std::for_each
can return void
without loss of functionality. Similar changes (change from passing by value to passing by forwarding reference and change all invocations to calling std::forward
ed function object instead of just non-const-lvalue) are possible for all the rest <numeric>
's and <algorithm>
's algorithms.
I know a partial workaround: is to pass object, wrapped by std::ref
(or std::cref
to enforce const this
), but there are issues with forwarding operator ()
cv-ref-qualifiers from wrapped functional object to wrapper's operator ()
.
Another workaround is to explicitly specify reference argument type into alorithm's template parameter list, but currently Function
parameter sadly always follows the InputIterator
and OutputIterator
parameters in the list:
#include <iostream>
#include <list>
#include <algorithm>
#include <iterator>
#include <utility>
#include <cstdlib>
int main()
{
std::list< int > l{{1, 2, 3, 4}};
std::copy_n(std::cbegin(l), l.size(), std::ostream_iterator< int >(std::cout, " "));
std::cout << std::endl;
struct F
{
int state;
void operator () (int i) { state += i; }
} f{0};
using I = std::list< int >::const_iterator;
std::for_each< I, F && >(std::cbegin(l), std::cend(l), std::move(f));
std::cout << f.state << std::endl;
return EXIT_SUCCESS;
}
By the way the change would allow to pass a non-copyable and/or non-moveable function objects to the algorithms w/o wrapping them.
See Question&Answers more detail:os