surfcode
surfcode

Reputation: 465

How to use std::partial_sum and output to a std::map?

I want an output map that has { {0,1},{1,2},{2,3},{3,4},{4,5} }, c++11 only. Any ideas?

std::map<int, int> m, out;
for( auto i=0; i < 5; ++i ) 
    m[i] = 1;

std::partial_sum( m.begin(), m.end(), std::inserter( out, out.begin() ),
        []( const std::pair<int,int>& a, const std::pair<int,int>& b ) 
             { return std::pair<int,int>( a.first, a.second + b.second ); } 
);

This gives compile error:

/usr/include/c++/5/bits/stl_pair.h: In instantiation of ‘std::pair<_T1, _T2>& std::pair<_T1, _T2>::operator=(std::pair<_U1, _U2>&&) [with _U1 = int; _U2 = int; _T1 = const int; _T2 = int]’:
/usr/include/c++/5/bits/stl_numeric.h:295:12:   required from ‘_OutputIterator std::partial_sum(_InputIterator, _InputIterator, _OutputIterator, _BinaryOperation) [with _InputIterator = std::_Rb_tree_iterator<std::pair<const int, int> >; _OutputIterator = std::insert_iterator<std::map<int, int> >; _BinaryOperation = main()::<lambda(const std::pair<int, int>&, const std::pair<int, int>&)>]’
../src/test_cumsum.cpp:43:130:   required from here
/usr/include/c++/5/bits/stl_pair.h:188:10: error: assignment of read-only member ‘std::pair<const int, int>::first’
first = std::forward<_U1>(__p.first);

Upvotes: 4

Views: 410

Answers (1)

Miles Budnek
Miles Budnek

Reputation: 30579

You can't. Not directly, at least. The issue is that std::map<int, int>::iterator::value_type is std::pair<const int, int>, and that const prevents an object of that type from being assigned to.

--

Take a look at this possible implementation for std::partial_sum:

template<class InputIt, class OutputIt, class BinaryOperation>
constexpr // since C++20
OutputIt partial_sum(InputIt first, InputIt last, 
                     OutputIt d_first, BinaryOperation op)
{
    if (first == last) return d_first;
 
    typename std::iterator_traits<InputIt>::value_type sum = *first;
    *d_first = sum;
 
    while (++first != last) {
       sum = op(std::move(sum), *first); // std::move since C++20
       *++d_first = sum;
    }
    return ++d_first;
}

Note that sum gets modified each iteration by assigning the result of op. Because sum.first is const that's not possible; hence the compilation error.

--

What you could do is define an iterator type that wraps std::map::iterator and strips out the const. For example, the following will work:

template <typename Pair>
struct RemoveFirstConstHelper
{
    using type = Pair;
};

template <typename T1, typename T2>
struct RemoveFirstConstHelper<std::pair<const T1, T2>>
{
    using type = std::pair<T1, T2>;
};

template <typename MapIterator>
class RemoveFirstConstIter
{
public:
    using difference_type = std::ptrdiff_t;
    using value_type = typename RemoveFirstConstHelper<typename MapIterator::value_type>::type;
    using pointer = value_type*;
    using reference = value_type;
    using iterator_category = std::input_iterator_tag;
    
    RemoveFirstConstIter(MapIterator it) : it_{it} {}
    
    reference operator*()
    {
        return *it_;
    }
    
    RemoveFirstConstIter& operator++()
    {
        ++it_;
        return *this;
    }
    
    RemoveFirstConstIter operator++(int) const
    {
        RemoveFirstConstIter temp{*this};
        ++temp;
        return temp;
    }
    
    bool operator==(const RemoveFirstConstIter& other) const
    {
        return it_ == other.it_;
    }
    
    bool operator!=(const RemoveFirstConstIter& other) const
    {
        return !(*this == other);
    }
    
private:
    MapIterator it_;
};

Live Demo

Or you could just write your own partial_sum implementation for maps. That seems like it would be simpler to me.

Upvotes: 3

Related Questions