Суммирование элементов структуры внутри вектора
Рассмотрим следующее:
typedef struct {
int a;
int b;
int c;
int d;
} ABCD;
typedef std::vector<ABCD> VecABCD;
Скажем, я хотел добавить каждый элемент 'a' в вектор типа VecABCD. Легко! Я просто петлю через вектор и суммирую, когда я иду.
int CalcSumOfA(const VecABCD &vec)
{
int sumOfA = 0;
VecABCD::const_iterator it;
for(it=vec.begin();it!=vec.end();it++)
sumOfA += it->a;
return sumOfA;
}
Скажем, я хотел сделать то же самое с "b"? Легко! Я бы написал... по сути, ту же функцию, но только с незначительными изменениями. То же самое с 'c' и 'd'.
Итак, есть ли более сжатый, менее повторяющийся способ сделать это? Я хотел бы сделать что-то вроде:
int sumOfA = SumOfMembers(myVec, a);
но я не могу представить, как бы я соединил такую функцию. В идеале это был бы шаблон, и я мог бы использовать его с вектором любой структуры, не связанным с VecABCD. У кого-нибудь есть идеи?
Ответы
Ответ 1
Суммирование STL может быть выполнено с помощью std::accumulate
#include <functional>
accumulate(v.begin(), v.end(), 0, bind(plus<int>(), _1, bind(&ABCD::a, _2)))
Если вы хотите, чтобы это было более общим, вы можете взять tr1:: функцию для члена, который вы хотите связать:
int sum_over_vec(const vector<ABCD>& v, const tr1::function<int (const ABCD&)>& member)
{
return accumulate(v.begin(), v.end(),
0,
bind(plus<int>(),
_1,
bind(member, _2)));
};
// ...
int sum_a = sum_over_vec(vec, bind(&ABCD::a, _1));
Другой способ сделать это, вместо того чтобы поместить вашу логику в функтор, - это поставить логику в итератор, используя итератор boost:: transform:
tr1::function<int (const ABCD&)> member(bind(&ABCD::a, _1));
accumulate(make_transform_iterator(v.begin(), member),
make_transform_iterator(v.end(), member),
0);
EDITED TO ADD: синтаксис лямбда С++ 11
Это становится несколько яснее с lambdas С++ 11 (хотя, к сожалению, не короче):
accumulate(v.begin(), v.end(), 0,
[](int sum, const ABCD& curr) { return sum + curr.a });
и
int sum_over_vec(const vector<ABCD>& v, const std::function<int (const ABCD&)>& member)
{
return accumulate(v.begin(), v.end(), 0,
[&](int sum, const ABCD& curr) { return sum + member(curr}); });
};
Использование:
// Use a conversion from member function ptr to std::function.
int sum_a = sum_over_vec(vec, &ABCD::a);
// Or using a custom lambda sum the squares.
int sum_a_squared = sum_over_vec(vec,
[](const ABCD& curr) { return curr.a * curr.a; });
Ответ 2
Другой вариант - использовать указатель-к-членам:
int CalcSumOf(const VecABCD & vec, int ABCD::*member)
{
int sum = 0;
for(VecABCD::const_iterator it = vec.begin(), end = vec.end(); it != end; ++it)
sum += (*it).*member;
return sum;
}
...
int sumA = CalcSumOf(myVec, &ABCD::a); // find sum of .a members
int sumB = CalcSumOf(myVec, &ABCD::b); // find sum of .b members
// etc.
Ответ 3
Вы можете использовать for_each. Его вариант.
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef struct{
int a;
}ABCD;
typedef vector<ABCD> vecABCD;
struct sum : public unary_function<ABCD, void>
{
sum(){count.a=count.b=count.c=count.d=0;}
void operator() (ABCD x) {
count.a+=x.a;
count.b+=x.b;
count.c+=x.c;
count.d+=x.d;
}
ABCD count;
};
int main()
{
ABCD s1={1,2,3,4};
ABCD s2={5,6,7,8};
vecABCD v;
v.push_back(s1);
v.push_back(s2);
sum s = for_each(v.begin(), v.end(), sum());
cout<<s.count.a<<endl;
}
выход:
4
Ответ 4
Используйте std:: accumulate:)
Ответ 5
Добавьте еще один вариант, к сожалению, уродливый. Можно взять относительный адрес от начала структуры ABCD к члену с помощью offsetof-функции. Поставить возвращаемое значение в функцию, и он может делать подсчет с использованием относительного местоположения с начала каждой структуры. Если ваши типы могут отличаться от int, вы можете также предоставить информацию о размере.