Какой лучший способ суммировать результат функции-члена для всех элементов в контейнере?

Скажем, у меня есть следующий объект:

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

Какой лучший способ (наиболее удобный, читаемый и т.д.) для получения всего size всех объектов в vector<Foo>? Я отправлю свое решение, но меня интересуют лучшие идеи.

Update:

До сих пор мы имеем:

  • std:: accumulate и функтор
  • std:: accumulate и выражение lambda
  • plain ol 'for-loop

Существуют ли какие-либо другие работоспособные решения? Можете ли вы сделать что-то поддерживаемое с помощью boost::bind или std::bind1st/2nd?

Ответ 1

В дополнение к вашему собственному предложению, если ваш компилятор поддерживает лямбда-выражения С++ 0x, вы можете использовать эту более короткую версию:

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();});

Ответ 2

Используйте std:: accumulate и функтор.

#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());

Ответ 3

Я нахожу Boost iterators elegants, хотя они могут быть немного подробными (алгоритмы на основе диапазонов сделают это лучше). В этом случае преобразует итераторы может выполнить задание:

#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);

Изменить: заменить "boost::bind(&Foo::size,_1)" на "std::mem_fn(&Foo::size)"

Изменить: я только что обнаружил, что библиотека Boost.Range обновлена, чтобы ввести алгоритмы диапазона! Вот новая версия того же решения:

#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);

Примечание: результаты примерно одинаковы (см. мой комментарий): внутри, transformed использует transorm_iterator.

Ответ 4

Ниже приведено решение проблемы:

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

Ответ 5

с использованием С++ 11 (и за его пределами), основанного на диапазоне для цикла

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