c++ - Passing function objects into std algorithms by reference -
isn't better pass function objects stl algorithms forwarding reference rather value? allow 1 utilize ref-qualifiers of operator ()
of function objects passed.
there couple of questions std::for_each
algorithm on (1, 2), considering problem changing of observable state of function object passed std::for_each
.
passing functional objects lvalue leference solve problem side effect of algorithms, can't return functional object (due should return, say, output iterator last value or else).
for example algorithm std::for_each
can changed (copied libc++):
template<typename _inputiterator, typename _function> _function for_each(_inputiterator __first, _inputiterator __last, _function __f) { (; __first != __last; ++__first) __f(*__first); return _glibcxx_move(__f); }
to:
template<typename _inputiterator, typename _function> _function && for_each(_inputiterator __first, _inputiterator __last, _function && __f) { (; __first != __last; ++__first) _glibcxx_forward(_function, __f)(*__first); return _glibcxx_forward(_function, __f); }
or (if such breaking changing allowed) std::for_each
can return void
without loss of functionality. similar changes (change passing value passing forwarding reference , change invocations calling std::forward
ed function object instead of non-const-lvalue) possible rest <numeric>
's , <algorithm>
's algorithms.
i know partial workaround: pass object, wrapped std::ref
(or std::cref
enforce const this
), there issues forwarding operator ()
cv-ref-qualifiers wrapped functional object wrapper's operator ()
.
another workaround explicitly specify reference argument type alorithm's template parameter list, function
parameter sadly follows inputiterator
, outputiterator
parameters in 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 = 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 way change allow pass non-copyable and/or non-moveable function objects algorithms w/o wrapping them.
isn't better pass function objects stl algorithms forwarding reference rather value?
yes, better. , better if there requirement functor need not copyconstructible
, copyassignable
, moveconstructible
or moveassignable
. standard says in 25.1:
note: unless otherwise specified, algorithms take function objects arguments permitted copy function objects freely. programmers whom object identity important should consider using wrapper class points noncopied implementation object such
reference_wrapper<t>
(20.9.4), or equivalent solution. — end note]
this issue considered way in 1998 lwg 92. , @ time note quote above added (the note has since been modified reference_wrapper<t>
didn't exist @ time).
this resolution vendors of std::lib, , resolution members of committee had job of fixing specification, not people such wanting use stateful functors.
and of course, @ time, forwarding references weren't available possible solution. @ time, common std::implementations pass functor around value within algorithm, further destroy state (as demonstrated in description of lwg 92.
you have correctly touched upon of points connected issue:
clients can use
std::ref
instead, won't respect reference-qualified functors.clients can explicitly specify functor reference parameters, won't prohibit implementations copying functor within algorithm.
explicitly specifying functor reference parameters extremely inconvenient client since ordered last in template parameter list.
fwiw, libc++ std::implementation written forbade internally copying functors. i.e. if code lwg 92 example:
#include <algorithm> #include <iostream> #include <list> #include <numeric> template <class c> void display(const c& c) { std::cout << '{'; if (!c.empty()) { auto = c.begin(); std::cout << *i; (++i; != c.end(); ++i) std::cout << ", " << *i; } std::cout << '}' << '\n'; } class nth { // function object returns true nth element private: int nth; // element return true int count; // element counter public: nth (int n) : nth(n), count(0) { } bool operator() (int) { return ++count == nth; } }; int main() { std::list<int> coll(10); std::iota(coll.begin(), coll.end(), 0); display(coll); auto pos = std::remove_if(coll.begin(), coll.end(), nth{3}); coll.erase(pos, coll.end()); display(coll); }
the results today are:
libc++
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} {0, 1, 3, 4, 5, 6, 7, 8, 9}
g++
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} {0, 1, 3, 4, 6, 7, 8, 9}
vs-2015
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} {0, 1, 3, 4, 6, 7, 8, 9}
g++'s libstdc++ , vs-2015 still copying nth
internal remove_if
described 18 years ago nico josuttis.
changing code to:
nth pred{3}; auto pos = std::remove_if(coll.begin(), coll.end(), std::ref(pred));
does portably change results to:
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} {0, 1, 3, 4, 5, 6, 7, 8, 9}
imho, run-time error waiting happen programmers not familiar long history of std::lib.
Comments
Post a Comment