Reputation: 16168
I was reading following question:
How to sum up elements of a C++ vector?, and I wanted to use second method (sum_of_elems =std::accumulate(vector.begin(),vector.end(),0);//#include <numeric>
).
However, I don't have std::vector<int>
, but std::vector<struct packet>
. The packet
is defined as following:
struct packet {
/// ...
int length() const;
///
}
and I want sum of packet lengths.
This is what I tried:
std::vector<packet> packets;
...
std::accumulate(packets.begin(), packets.end(), 0, [](const packet& a, const packet& b){return a.length() + b.length();});
but it doesn't work. In C#, I'd write something like
packet[] p;
p.Select(x => p.length()).Sum();
Is it possible to do something like that in C++? I can write method for iterating through the vector and doing it on my own, but I prefer the functional approach where possible.
Upvotes: 2
Views: 308
Reputation: 299940
I would note that the C# implementation is slightly different, in essence.
In C++ you are trying to add int
and packet
whilst in C# you first provide a transformation step from packet
to int
and then add int
s.
The equivalent C++, without adaptation:
std::vector<size_t> lengths; // a length cannot be negative!
std::transform(packets.begin(),
packets.end(),
backward_inserter(lengths),
[](packet const& p) { return p.length(); });
auto const sum = std::accumulate(lengths.begin(), lengths.end(), 0ul);
Of course, it is wasteful to store the intermediate lengths, however it does work out of the box.
But because we are cool, let us have look at Boost.Range
, and more precisely:
Which have a bit of coolness like Linq:
#include <boost/range/numeric.hpp> // accumulate
#include <boost/range/adaptor/transformed.hpp>
size_t total_size(std::vector<packet> const& packets) {
return boost::accumulate(
packets | boost::transformed([](packet const& p) { return p.length(); }),
0ul);
}
Upvotes: 4
Reputation: 9380
Apart from lamba, you can change it to
std::accumulate(packets.begin(), packets.end(), 0, packet());
Where you can define functor as:
int operator() (int result, const packet& obj)
{
return result+ obj.length();
}
Upvotes: 1
Reputation: 254501
The first parameter of the accumulate operation is the running total. In your case, this is an integer, not a packet, so your lambda should be
[](int a, const packet& b) {return a + b.length();}
Upvotes: 2
Reputation: 4696
The problem is your accumulate function. Its first parameter has to be of the type you're trying to accumulate (int
in this case) and add a value on top of that.
Your lambda function should look like this: [](int currTotal, const packet& b){return currTotal + b.length();}
Upvotes: 1
Reputation: 275575
You are accumulating via a binary operation. Your accumulated value starts with 0
(an int
), so the left hand side of your binary operation has to be convertible-from 0
-- otherwise, how does it start adding?
Try this:
std::accumulate(
packets.begin(),
packets.end(),
0,
[](int a, const packet& b){
return a + b.length();
}
);
you can also do this via a simple loop:
int acc = 0;
for( const packet& p : packets ) {
acc += packets.length();
}
Upvotes: 3