开发者

understanding Functors in STL

开发者 https://www.devze.com 2023-01-05 06:30 出处:网络
quoting from \"The C++ Standard Library\" by N M Jousttis, Section 5.9 #include < iostream> #include &l开发者_运维技巧t; list>

quoting from "The C++ Standard Library" by N M Jousttis, Section 5.9


#include < iostream>
#include &l开发者_运维技巧t; list>
#include < algorithm>

using namespace std;

//function object that adds the value with which it is initialized
class AddValue {
    private:
       int the Value; //the value to add
    public:
       //constructor initializes the value to add
       AddValue(int v) : theValue(v) {    }
       //the "function call" for the element adds the value
       void operator() (int& elem) const {  elem += theValue; }
 };

int main()
{
      list<int> coll;
      for (int i=1; i<=9; ++i) 
         coll.push_back(i); 

      //The first call of for_each() adds 10 to each value:
      for_each (coll.begin(), coll.end(), AddValue(10)) ; 

Here, the expression AddValue(10) creates an object of type AddValue that is initialized with the value 10. The constructor of AddValue stores this value as the member theValue. Inside for_each(), "()" is called for each element of coll. Again, this is a call of operator () for the passed temporary function object of type AddValue. The actual element is passed as an argument. The function object adds its value 10 to each element. The elements then have the following values: after adding 10:

11 12 13 14 15 16 17 18 19

The second call of for_each() uses the same functionality to add the value of the first element to each element. It initializes a temporary function object of type AddValue with the first element of the collection:

for_each (coll.begin(), coll.end(), AddValue (*coll. begin()) ) ; 

The output is then as follows after adding first element:

22 23 24 25 26 27 28 29 30

what I don't understand is in the second case why is the output is not

22 34 35 36 37 38 39 40 41

meaning is a new functor being created for each call or is the functor used for each call ?


The expression AddValue(*coll. begin()) creates one temporary object of class AddValue. That temporary is then passed to the for_each function. for_each then calls the object's function call operator - that is, operator() - once for each element from coll.begin() to coll.end().

Technically, for_each takes the functor parameter by value (not reference), so a it actually operates on a copy of the temporary, not the temporary itself.


[Edit] I originally misunderstood the author's original intent. I corrected the mistake.

for_each (coll.begin(), coll.end(), AddValue (*coll. begin()) ) ;

The output is then as follows after adding first element:

22 23 24 25 26 27 28 29 30

what I don't understand is in the second case why is the output is not

22 34 35 36 37 38 39 40 41

meaning is a new functor being created for each call or is the functor used for each call ?

First of all, regardless of whether the for_each algorithm copies the functor you pass in is irrelevant. What's relevant is that you should be storing the iterator or pointer to the first element rather than the pointee. If you do that and dereference it each time, it should do the trick.

struct AddValue 
{
    const int* ptr;
    explicit AddValue(const int* iptr): ptr(iptr) {}
    void operator() (int& elem) const {elem += *ptr; }
};

int main()
{
    vector<int> v;
    for (int j=1; j <= 9; ++j)
        v.push_back(j + 10);
    for_each(v.begin(), v.end(), AddValue(&v[0]) );
    // v will be [22 34 35 36 37 38 39 40 41]
}

However, I'm going to have to throw in my two cents about functional programming. You can get really concise, cleverly-tight code through functional programming. However, it's also a big pain to debug and has the effect of decentralizing code. Unless your code can significantly benefit from it, consider simple iterator-based for loops when you can as it'll give your fellow programmers a much easier time debugging and reading through your code.

I made the mistake of getting super heavy-handed with functional programming in the past and it made my team hate me. I was obsessed with writing devilish tight code with combinatorial predicate logic and built huge libraries of function objects that could be reused over and over and in combinations. Really all I did was spend a lot of time writing functors when I could have been writing simple, equally reusable functions instead that could have been called (and inlined just as easily) from a simple iterator-based for loop (even easier to write with C++0x's range-based for loop and BOOST_FOR_EACH). I still use functional programming in C++, but sparingly. When you're going through a lot of trouble and piling on to build times to make two or three lines of code into one, you really have to ask yourself and think deeply if it's worth it and not just for yourself, but for everyone working with your code.


Yes, it is what you said. Functors are passed by value by default and thus they are copied in code of std::for_each. However you could write your own version of std::for_each explicitly stating that you want to pass functor by reference.


Yes. A new copy of your functor is passed allong through for_each. The book you're reading explains this.


I believe the reason why the functor is copied around like this is to make for_each more general.

Imagine an implementation that took the functor by reference. That would break if the functor is an rvalue (say, if it is returned from another function):

std::for_each(first, last, get_functor(...))

Of course, in those cases, the algorithm could take the functor by const reference (since they can be bound to rvalues), but then the functor has to be const.

The only really general solution is to pass the functor by value. Then it works with const, non-const, rvalue and lvalue functors.


Your AddValue constructor takes an int so when you construct it from *coll.begin() then value of the first member of your collection is used to initialize the member variable theValue.

This is now fixed (nothing else modifies theValue) so everytime theValue is used from this AddValue object or any copies of this AddValue object it will still have the same value with which it was initialized.

This is the value that *coll.begin() had at the time the first AddValue object was constructed, not the value *coll.begin() might have been modified to.

0

精彩评论

暂无评论...
验证码 换一张
取 消