Reputation: 24719
I always try to incorporate STL algorithms wherever I can, rather than writing manual loops. However, I'm having difficulty understanding how std::accumulate
is generally useful. Whenever I need to calculate sums or averages, I almost always end up resorting to manual loops, because I have difficulty getting std::accumulate
to do what I need.
The problem is that I rarely ever have a simple vector of integers that need to be summed. Usually, I want to sum an array of objects using a particular member variable. Yes, I know there is a version of std::accumulate
that takes a BinaryFunction, but the problem I see is that this function needs to take two values of type T
, where T
is the type of the sum, rather than the type of the operands. I'm having trouble understanding how this is useful.
Consider a case which I assume is pretty common. I have the following class:
struct Foo
{
Foo(int cost_, int id_) : cost(cost_), id(id_)
{ }
int cost;
int id;
};
Now, say I want to calculate the sum of an array of Foo
objects, using Foo::cost
.
I want to say:
std::vector<Foo> vec;
// fill vector with values
int total_cost = std::accumulate(vec.begin(), vec.end(), 0, sum_cost);
And sum_cost
is defined as:
int sum_cost(const Foo& f1, const Foo& f2)
{
return f1.cost + f2.cost;
}
The problem is, this doesn't work because std::accumulate
expects a BinaryFunction which takes in two instances of the resulting sum type - which in this case is just int
. But how is that useful to me? If my BinaryFunction takes in two int
s, I can't specify that I want to sum the cost
field.
So, why is std::accumulate
designed this way? Am I just not seeing something obvious here?
Upvotes: 19
Views: 9408
Reputation: 40859
You're wrong about accumulate operator taking two of the same type. It does that only if you want to. The use the operator is specifically sum = op(sum, *iter)
. Thus your code:
int count = std::accumulate(stuff.begin(), stuff.end(), 0,
[](int current_sum, stuff_value_t const& value) {
return current_sum + value.member;
});
If you can't use a lambda, then of course you use the standard binders or boost::bind.
Upvotes: 27
Reputation: 51465
use functor:
class F { // sum Foos
F(int init = 0);
template<class T>
Foo operator()(const Foo &a, const T &b) const;
operator int() const;
};
int total_cost = std::accumulate(vec.begin(), vec.end(), F(0), F());
notice you can do other things as well:
class F { // sum foo values members
template<class T>
T operator()(const T &a, const Foo &b) const;
};
int total_cost = std::accumulate(vec.begin(), vec.end(), int(0), F());
Upvotes: 3