Projecting on a std::map with std::ranges::min

1k views Asked by At

Is it possible to project on a std::map? I tried to use std::ranges::min with a projection, but it seems to throw errors that I can't interpret why its not liking things.

#include <set>
#include <iostream>
#include <ranges> 
#include <vector>
#include <iostream>
#include <map>
#include <algorithm>

int main()
{
    std::map<int, int> usage_table;
    auto lowest = std::ranges::min( std::move(usage_table), 
                                   {}, 
                                   &std::map<int,int>::value_type::second );
}

I could work around it, but it would be nice if this one liner worked.

Best

2

There are 2 answers

6
cigien On BEST ANSWER

Instead of std::ranges::min you can use std::ranges::min_element like this:

auto lowest = *std::ranges::min_element(std::move(usage_table), 
                                        {}, 
                                        &std::map<int,int>::value_type::second);

Also, it's unclear why you are moveing the map, it doesn't seem to do anything useful.

4
康桓瑋 On

Look at std::ranges::min's function signature:

template< ranges::input_range R, class Proj = std::identity,
          std::indirect_strict_weak_order<
              std::projected<ranges::iterator_t<R>, Proj>> Comp = ranges::less >
requires std::indirectly_copyable_storable<ranges::iterator_t<R>, ranges::range_value_t<R>*>
constexpr ranges::range_value_t<R> min( R&& r, Comp comp = {}, Proj proj = {} );

It returns range_value_t<R> which imply the value_type of range need be copyable, so ranges::min requires indirectly_copyable_storable which requires indirectly_copyable which requires indirectly_writable:

template<class Out, class T>
  concept indirectly_writable =
    requires(Out&& o, T&& t) {
      *o = std::forward<T>(t);
      *std::forward<Out>(o) = std::forward<T>(t);
      const_cast<const std::iter_reference_t<Out>&&>(*o) = std::forward<T>(t);
      const_cast<const std::iter_reference_t<Out>&&>(*std::forward<Out>(o)) =
        std::forward<T>(t);
    };

We need *o = std::forward<T>(t) be valid expression which Out is range_value_t<R>* which is std::pair<const int, int>*, but this is not possible since we can't assign a std::pair<const int, int> to other:

std::pair<const int, int> a, b;
// use of deleted function 'std::pair<const int, int>& std::pair<const int, int>::operator=(const std::pair<const int, int>&)'
a = b;

So unfortunately, std::ranges::min cannot apply in std::map:

// constraints not satisfied
std::ranges::min(std::map<int, int>{});

But if you want to find std::map's minimum value of its key_type/mapped_type, you can just use c++20 range adaptor std::views::keys/values, it works fine with std::ranges::min:

std::map<int, int> usage_table{{5, -12}, {3, 4}};
// -12
auto lowest = std::ranges::min(usage_table | std::views::values);