Search code examples
c++c++11dictionaryaccumulate

Adding all values of map using std::accumulate


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'


Solution

  • 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