2

I am having trouble using function-objects in Visual Studio 2012.

I created a simple std::vector, added the ints 0-9 and wanted to create the sum of it using a function object. My class definition (inline):

template <class T>
class Sum {
private:
    T val;
public:
    Sum (T i = 0) : val(i) {
    }

    void operator()(T x) { 
        val += x; 
    }

    T result() const { 
        return val; 
    }

    ~Sum() {
        std::cout << "Invoked destructor! val: " << val << " for this: " << this << std::endl;
    }
};

My main function:

int main(int argc, char *argv[]){

    Sum<int> s;

    int contents[] = {1,2,3,4,5,6,7,8,9};

    std::vector<int> vec = std::vector<int>(contents, contents + 9);

    std::for_each(vec.begin(), vec.end(), s);

    std::cout << "Sum of all numbers: " << s.result() << std::endl;

    return 0;
}

Using the output from the destructor, I'll get:

Invoked destructor! val: 45 for this: 003BFDA4
Invoked destructor! val: 45 for this: 003BFDE0
Sum of all numbers: 0
Invoked destructor! val: 0 for this: 003BFEFC

Is this a bug from VS? Running through it using the debugger, the items are summed up to 45 but immediately afterwards the destructor is called. What am I doing wrong?

EDIT:

This is an example from Stroustrup's The C++ Programming Language, chapter 18.4. I just wondered it didn't work, as I copied it exactly.

bash.d
  • 13,029
  • 3
  • 29
  • 42
  • 2
    Are you aware of the `std::accumulate` function template from ``? – Armen Tsirunyan Mar 19 '13 at 20:46
  • @ArmenTsirunyan Thank you for your answer, I edited my post, explaining the reason behind this. – bash.d Mar 19 '13 at 20:50
  • 1
    I just check my copy of the book from 18.4: `s = for_each(ld.begin(), ld.end(), s);`. It seems you didn't follow the book correctly. – Jesse Good Mar 19 '13 at 20:52
  • @JesseGood In the book he calls the method in a function, but this shouldn't make any difference, if I have a reference to a Container or the Container itself, should it? – bash.d Mar 19 '13 at 20:54
  • @bash.d: No, it won't make a difference in the code. The difference is whether you properly handle the return from `for_each`. – Jesse Good Mar 19 '13 at 20:56
  • @JesseGood Did you copy this line exactly from the book? I don't have `s = ` in my edition! This would surely explain the error. – bash.d Mar 19 '13 at 20:58
  • @bash.d: Yep, it's in the [errata for the 4th printing](http://www.stroustrup.com/3rd_printing5.html): `pg 515 s/for_each(ld.begin(),ld.end(),s);/s = for_each(ld.begin(),ld.end(),s);/` – Jesse Good Mar 19 '13 at 21:01
  • @JesseGood Thank you very much, I won't forget this ever again! – bash.d Mar 19 '13 at 21:01
  • @Downvoter what is this??? – bash.d Apr 22 '13 at 13:03

1 Answers1

5

The problem is that std::for_each accepts your functor argument by value. This means it works on a copy of your original object. The good news is that it also returns that copy which holds the modified state. This should do the trick:

Sum<int> s1 = std::for_each(vec.begin(), vec.end(), s);
std::cout << "Sum of all numbers: " << s1.result() << std::endl;

Alternatively, you could let the val in your functor be a reference to some variable:

template <class T>
class Sum {
private:
    T& val;
public:
    Sum (T& i) : val(i) {
    }
// ...

Now, this should work:

int i = 0;
Sum<int> s(i);
std::for_each(vec.begin(), vec.end(), s);
std::cout << "Sum of all numbers: " << s1.result() << std::endl;

But you will have to take care of making sure that the lifetime of i is sufficiently extended not to make Sum<T>::val a dangling reference.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Thank you, this works. It makes me wonder, though, as I copied this example from Stroustrup's `The C++ Programming Language`, chapter 18.4 – bash.d Mar 19 '13 at 20:47
  • @bash.d: Sure you copied everything correctly? Maybe that's a mistake in the book, but I would tend to doubt it. Unless there is an Errata Corrige for it something (maybe on the website?) – Andy Prowl Mar 19 '13 at 20:50
  • Yes, I am certain, only my variable's name is different. If you got the book (3rd edition), have a look at it. I haven't looked for errata, I expected this simple example to work out-of-the-box. – bash.d Mar 19 '13 at 20:52
  • @bash.d: Unfortunately I do not have a copy of that book here, but it's possible that this is just a mistake. Good to know that it happens to Him as well apparently :) – Andy Prowl Mar 19 '13 at 20:56
  • well, He is human, too :) As JesseGood stated in the comments: I don't have `s = ` printed in my edition, so it couldn't work in the beginning! Thank you for your help. – bash.d Mar 19 '13 at 21:01
  • I guess an `std::reference_wrapper` could also be an alternative. – juanchopanza Mar 19 '13 at 21:06