Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::accumulate using the view std::ranges::views::values

Is there any way to make that code compile, or I have to create my own lambda as fourth parameter of std::accumulate?

#include <iostream>
#include <ranges>
#include <unordered_map>
#include <numeric>

namespace rv = std::ranges::views;

int main()
{
    std::unordered_map<unsigned, unsigned> m = {{5, 3}};
    
    auto values = m | rv::values;
    
    std::cout << std::accumulate(values.begin(), values.end(), 0) << std::endl;
}

The error that gcc throws, if I understood it correctly, basically says that begin and end yields different types and std::accumulate cannot deduce a unique InputIterator type. The full compiler output is:

main.cpp:15:65: error: no matching function for call to 'accumulate(std::ranges::elements_view<std::ranges::ref_view<std::unordered_map<unsigned int, unsigned int> >, 1>::_Iterator<true>, std::__detail::_Node_iterator<std::pair<const unsigned int, unsigned int>, false, false>, int)'
   15 |     std::cout << std::accumulate(values.begin(), values.end(), 0) << std::endl;
      |                                                                 ^
In file included from /usr/local/include/c++/10.2.0/numeric:62,
                 from main.cpp:5:
/usr/local/include/c++/10.2.0/bits/stl_numeric.h:134:5: note: candidate: 'template<class _InputIterator, class _Tp> constexpr _Tp std::accumulate(_InputIterator, _InputIterator, _Tp)'
  134 |     accumulate(_InputIterator __first, _InputIterator __last, _Tp __init)
      |     ^~~~~~~~~~
/usr/local/include/c++/10.2.0/bits/stl_numeric.h:134:5: note:   template argument deduction/substitution failed:
main.cpp:15:65: note:   deduced conflicting types for parameter '_InputIterator' ('std::ranges::elements_view<std::ranges::ref_view<std::unordered_map<unsigned int, unsigned int> >, 1>::_Iterator<true>' and 'std::__detail::_Node_iterator<std::pair<const unsigned int, unsigned int>, false, false>')
   15 |     std::cout << std::accumulate(values.begin(), values.end(), 0) << std::endl;
      |                                                                 ^
In file included from /usr/local/include/c++/10.2.0/numeric:62,
                 from main.cpp:5:
/usr/local/include/c++/10.2.0/bits/stl_numeric.h:161:5: note: candidate: 'template<class _InputIterator, class _Tp, class _BinaryOperation> constexpr _Tp std::accumulate(_InputIterator, _InputIterator, _Tp, _BinaryOperation)'
  161 |     accumulate(_InputIterator __first, _InputIterator __last, _Tp __init,
      |     ^~~~~~~~~~
/usr/local/include/c++/10.2.0/bits/stl_numeric.h:161:5: note:   template argument deduction/substitution failed:
main.cpp:15:65: note:   deduced conflicting types for parameter '_InputIterator' ('std::ranges::elements_view<std::ranges::ref_view<std::unordered_map<unsigned int, unsigned int> >, 1>::_Iterator<true>' and 'std::__detail::_Node_iterator<std::pair<const unsigned int, unsigned int>, false, false>')
   15 |     std::cout << std::accumulate(values.begin(), values.end(), 0) << std::endl;
      |                                                                 ^

like image 412
Peregring-lk Avatar asked Sep 03 '20 20:09

Peregring-lk


1 Answers

The error that gcc throws, if I understood it correctly, basically says that begin and end yields different types

Indeed. In Ranges terms, elements_view's iterator and sentinel are different types. Before C++20, these types had to be the same, and lots of code is written assuming this. Unfortunately, we do not yet have ranges::accumulate which would handle this properly for you.

Until then, there is another range adapter which forces a range to have the same type for its iterator and sentinel (or be a no-op if that is already the case): views::common:

auto values = m | rv::values | rv::common;

Which in this case would create a view of common_iterators, which is basically a variant<iterator, sentinel> wrapped up to behave like an iterator.

like image 157
Barry Avatar answered Oct 19 '22 10:10

Barry