20

I am simply trying to add values of a map defined in the program below:

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;

I get the following error:

Error C2893: Failed to specialize function template 'unknown-type std::plus::operator ()(_Ty1 &&,_Ty2 &&) const'

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
Daqs
  • 720
  • 3
  • 8
  • 28

5 Answers5

36

std::begin(floor_plan) gives you an iterator pointing at std::map<int, int>::value_type which is std::pair<const int, int>. Since there is no operator+ defined for this pair type and an integer, your code fails to compile.

Option #1

If you want to sum up all the mapped values from floor_plan, you'd need to provide your own binary operator that is able to extract the second element of a dereferenced iterator passed in:

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

DEMO 1

Option #2

Alternatively, you could exploit the Boost.Iterator library to extract the second element of a pair on the fly with 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);

DEMO 2

Option #3

Another approach is to use the Boost.Range library along with its own implementation of the accumulate algorithm:

#include <boost/range/numeric.hpp>
#include <boost/range/adaptor/map.hpp>

boost::accumulate(floor_plan | boost::adaptors::map_values, 0);

DEMO 3

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
4

Piotr S. answer is right, but if this is not a one-time task, you better make a simple convenient functor for such tasks:

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

Thank to templated operator() you can use this functor for any map in your code. This is much like transparent comparator, but this is transparent "summator".

Brian
  • 8,454
  • 5
  • 27
  • 30
Mikhail
  • 20,685
  • 7
  • 70
  • 146
1

I will show you not only how does it works.

accumulate Possible implementation as below(as we may sum from a base value, so there is a init value):

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

So when we wants to get sum/product of a vector, it may like this:

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

As to std::map, you wants to sum the second value of an map, so you have to get each second item in map. So you should get the value_type in map and then get the second item. value_type in map is defined as below:

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

For example, get the sum of all the second/first value:

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

The para v in the above lambda funtion, stores the tmp sum, with init value 0.

Brian
  • 8,454
  • 5
  • 27
  • 30
Jayhello
  • 5,931
  • 3
  • 49
  • 56
0

Alternatively, you could use lambda expression with pair<int,int> as the binary operation.

distance = std::accumulate(std::begin(floor_plan), std::end(floor_plan), 0,
                   [](int value, pair<int,int> p) { return value + p.second; });
SridharKritha
  • 8,481
  • 2
  • 52
  • 43
0

Here is a simple example :

#include <iostream>
#include <map>
#include <numeric>

int main() {
  // Create a map with some initial key-value pairs
  std::map<std::string, int> m = {{"a", 1}, {"b", 2}, {"c", 3}};

  // Use std::accumulate to add up all of the values in the map
  int sum = std::accumulate(m.begin(), m.end(), 0,
    [](int total, const std::pair<std::string, int>& p) {
      return total + p.second;
    });

  // Print the sum of the values in the map
  std::cout << "The sum of the values in the map is " << sum << "\n";

  return 0;
}


  • This seems to be just a re-hash of the first part of the accepted answer. It doesn't add any new value to the question. – Toby Speight Dec 12 '22 at 15:04