Michael Kristofik
Michael Kristofik

Reputation: 35188

What's the best way to sum the result of a member function for all elements in a container?

Let's say I have the following object:

struct Foo
{
    int size() { return 2; }
};

What's the best way (most maintainable, readable, etc.) to get the total size of all objects in a vector<Foo>? I'll post my solution but I'm interested in better ideas.

Update:

So far we have:

Are there any other workable solutions? Can you make something maintainable using boost::bind or std::bind1st/2nd?

Upvotes: 15

Views: 6697

Answers (5)

Michael Miller
Michael Miller

Reputation: 81

using C++11 (and beyond) range-based for loop

std::vector<Foo> vFoo;
// populate vFoo with some values...
int totalSize = 0;
for (const auto& element: vFoo) {
    totalSize += element.size();
}

Upvotes: 6

Stack Overflow is garbage
Stack Overflow is garbage

Reputation: 248159

In addition to your own suggestion, if your compiler supports C++0x lambda expressions, you can use this shorter version:

std::vector<Foo> vf;

// do something to populate vf


int totalSize = std::accumulate(vf.begin(),
                                vf.end(),
                                0, 
                                [](int sum, const Foo& elem){ return sum + elem.size();});

Upvotes: 32

rafak
rafak

Reputation: 5551

I find Boost iterators elegants, although they can be a bit verbose (range-based algorithms would make this better). In this case transform iterators can do the job:

#include <boost/iterator/transform_iterator.hpp>
//...

int totalSize = std::accumulate(
    boost::make_transform_iterator(vf.begin(), std::mem_fn(&Foo::size)),
    boost::make_transform_iterator(vf.end(), std::mem_fn(&Foo::size)),0);

Edit: replaced "boost::bind(&Foo::size,_1)" by "std::mem_fn(&Foo::size)"

Edit: I just found that the Boost.Range library has been updated to introduce range algorithms! Here is a new version of the same solution:

#include <boost/range/distance.hpp> // numeric.hpp needs it (a bug?)
#include <boost/range/numeric.hpp> // accumulate
#include <boost/range/adaptor/transformed.hpp> // transformed
//...
int totalSize = boost::accumulate(
    vf | boost::adaptors::transformed(std::mem_fn(Foo::size)), 0);

Note: the performances are approximately the same (see my comment): internally, transformed uses transorm_iterator.

Upvotes: 7

Philipp
Philipp

Reputation: 49842

Here is the down-to-earth solution:

typedef std::vector<Foo> FooVector;
FooVector vf;
int totalSize = 0;
for (FooVector::const_iterator it = vf.begin(); it != vf.end(); ++it) {
  totalSize += it->size();
}

Upvotes: 4

Michael Kristofik
Michael Kristofik

Reputation: 35188

Use std::accumulate and a functor.

#include <functional>
#include <numeric>

struct SumSizes : public std::binary_function<int, Foo, int>
{
    int operator()(int total, const Foo& elem) const
    {
        return total + elem.size();
    }
};

std::vector<Foo> vf;

// do something to populate vf

int totalSize = std::accumulate(vf.begin(),
                                vf.end(),
                                0, 
                                SumSizes());

Upvotes: 8

Related Questions