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::forwarded 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

Popular posts from this blog

get url and add instance to a model with prefilled foreign key :django admin -

css - Make div keyboard-scrollable in jQuery Mobile? -

ruby on rails - Seeing duplicate requests handled with Unicorn -