Я просто пытаюсь добавить значения карты, определенной в программе ниже:
std::map<int, int> floor_plan;
const size_t distance = std::accumulate(std::begin(floor_plan), std::end(floor_plan), 0);
std::cout << "Total: " << distance;
Я получаю следующую ошибку:
Ошибка C2893: не удалось специализировать шаблон функции ‘неизвестный тип std :: plus :: operator () (_ Ty1 &&, _Ty2 &&) const ‘
std::begin(floor_plan)
дает вам итератор, указывающий на std::map<int, int>::value_type
который std::pair<const int, int>
, Так как нет operator+
определенный для этого типа пары и целого числа, ваш код не может быть скомпилирован.
Если вы хотите суммировать все сопоставленные значения из floor_plan
вам нужно будет предоставить свой собственный бинарный оператор, который сможет извлечь второй элемент разыменованного итератора, переданного в:
std::accumulate(std::begin(floor_plan)
, std::end(floor_plan)
, 0
, [] (int value, const std::map<int, int>::value_type& p)
{ return value + p.second; }
);
Кроме того, вы можете использовать Boost.Iterator библиотека для извлечения второго элемента пары на лету с boost::make_transform_iterator
:
#include <boost/iterator/transform_iterator.hpp>
#include <functional>
auto second = std::mem_fn(&std::map<int, int>::value_type::second);
std::accumulate(boost::make_transform_iterator(std::begin(floor_plan), second)
, boost::make_transform_iterator(std::end(floor_plan), second)
, 0);
Другой подход заключается в использовании Boost.Range библиотека вместе с собственной реализацией accumulate
алгоритм:
#include <boost/range/numeric.hpp>
#include <boost/range/adaptor/map.hpp>
boost::accumulate(floor_plan | boost::adaptors::map_values, 0);
Петр С. Ответ правильный, но если это не разовое задание, лучше сделать простой удобный функтор для таких заданий:
struct AddValues
{
template<class Value, class Pair>
Value operator()(Value value, const Pair& pair) const
{
return value + pair.second;
}
};
const size_t distance = std::accumulate(plan.begin(), plan.end(), 0, AddValues());
Спасибо operator()
Вы можете использовать этот функтор для любого map
в вашем коде. Это очень похоже на прозрачный компаратор, но это прозрачный «сумматор».
Я покажу вам не только, как это работает.
accumulate
Возможная реализация как ниже(как мы можем суммировать от базового значения, так что есть init
значение):
template<class InputIt, class T, class BinaryOperation>
T accumulate(InputIt first, InputIt last, T init,
BinaryOperation op)
{
for (; first != last; ++first) {
init = op(std::move(init), *first); // std::move since C++20
}
return init;
}
Поэтому, когда мы хотим получить sum/product
из vector
это может понравиться так:
vector<int> vec(5);
std::iota(vec.begin(), vec.end(), 1);
cout<<"vec: ";// output vec
std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(cout, ", "));
// vec: 1, 2, 3, 4, 5,
cout<<"\n vec sum is: "<<accumulate(vec.begin(), vec.end(), 0)<<endl;
// vec sum is: 15
cout<<"vec product is: "<<accumulate(vec.begin(), vec.end(), 1, std::multiplies<int>())<<endl;
// vec product is: 120
Относительно std::map
Вы хотите суммировать второе значение map
так что вы должны получить каждый second item
в карте. Так что вы должны получить value_type
в map
а затем получить второй предмет. value_type
в map
определяется как ниже:
template <typename Key, typename Value, class Compare = std::less<Key>>
class map{
// using re_tree to sort
typedef Key key_type;
// rb_tree value type
typedef std::pair<key_type, value_type> value_type;
};
Например, получить сумму всех second/first
значение:
typedef map<string, int> IdPrice;
IdPrice idPrice = {{"001", 100}, {"002", 300}, {"003", 500}};
int sum = accumulate(idPrice.begin(), idPrice.end(), 0, [](int v, const IdPrice::value_type& pair){
return v + pair.second;
// if we want to sum the first item, change it to
// return v + pair.first;
});
cout<<"price sum is: "<<sum<<endl; // price sum is: 900
Пара v
в выше lambda funtion
, хранит сумму tmp со значением init 0.