Необходимость суммировать поля из контейнера структур

Это больше вопрос кода, потому что у меня уже есть пример. Я делаю это в тонне кода, и создание всех этих лямбд (некоторые из которых являются одинаковыми) начинает раздражать меня.

Итак, учитывая структуру:

struct foo {
int b() const { return _b; }
int a() const { return _a; }
int r() const { return _r; }
const int _b;
const int _a;
const int _r;
};

У меня есть контейнер с указателями на них, скажем, vector<foo*> foosТеперь я хочу пройти через контейнер и получить сумму одного из полей.
В качестве примера, если я хотел поле _rтогда мой текущий подход заключается в том, чтобы сделать это:

accumulate(cbegin(foos), cend(foos), 0, [](const auto init, const auto i) { return init + i->r(); } )

Я пишу эту строку везде. Можно ли улучшить это? Я бы очень хотел написать что-то вроде этого:

x(cbegin(foos), cend(foos), mem_fn(&foo::r));

Я не думаю, что стандарт предусматривает что-то подобное. Я, конечно, мог бы написать это, но тогда читателю потребовалось бы выяснить мой подозрительный код, а не просто знать, что accumulate делает.

1

Решение

Вместо написания пользовательского накопления я предлагаю написать собственный генератор функторов, который возвращает функтор, который можно использовать в качестве аргумента для std::accumulate,

template<class Fun>
auto mem_accumulator(Fun member_function) {
return [=](auto init, auto i) {
return init + (i->*member_function)();
};
}

затем

accumulate(cbegin(foos), cend(foos), 0, mem_accumulator(&foo::r));

Несколько вариантов:

Для контейнеров предметов:

template<class MemFun>
auto mem_accumulator(MemFun member_function) {
return [=](auto init, auto i) {
return init + (i.*member_function)();
};
}

Используйте указатели на элементы данных вместо функций:

template<class T>
auto mem_accumulator(T member_ptr) {
return [=](auto init, auto i) {
return init + i->*member_ptr;
};
}
// ...
accumulator(&foo::_r)

Функторы поддержки, а не указатели на функции-члены:

template<class Fun>
auto accumulator(Fun fun) {
return [=](auto init, auto i) {
return init + fun(i);
};
}
// ...
accumulator(std::mem_fun(&foo::r))

Некоторые (все?) Из этих вариаций могут быть объединены для автоматического выбора с помощью некоторой магии SFINAE, но это увеличит сложность.

1

Другие решения

Существует действительно очень элегантный способ решить эту проблему, используя Шаблоны переменных которые были введены в . Мы можем шаблонизировать лямбда-переменную, используя указатель метода в качестве аргумента шаблона:

template <int (foo::*T)()>
auto func = [](const auto init, const auto i){ return init + (i->*T)(); };

Проходя func соответствующая специализация func в качестве последнего аргумента accumulate будет иметь тот же эффект, что и запись лямбды на месте:

accumulate(cbegin(foos), cend(foos), 0, func<&foo::r>)

Живой пример


Еще одна альтернатива, основанная на той же предпосылке шаблонизации, которая не требует , это шаблонизированная функция предложено StoryTeller:

template <int (foo::*T)()>
int func(const int init, const foo* i) { return init + (i->*T)(); }

Что также можно использовать, просто передав указатель на метод:

accumulate(cbegin(foos), cend(foos), 0, &func<&foo::r>)

Живой пример


Специфичность, требуемая обоими этими примерами, была удалена в где мы можем использовать auto для типов параметров шаблона: http://en.cppreference.com/w/cpp/language/auto Это позволит нам объявить func так что это может быть использовано любой класс, а не только foo:

template <auto T>
auto func(const auto init, const auto i) { return init + (i->*T)(); }
1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector