NoSenseEtAl
NoSenseEtAl

Reputation: 30038

std::multimap equal _range and C++20 std::views::values do not work nicely together

I have the following code, it works, but C++20 version does not look much better than C++17 version. My guess issue is that multimap equal_range returns a pair and ranges can not figure out that is a pair of valid iterators.

Is there a way to write this in a shorter nicer way?

#include <iostream>
#include <map>
#include <ranges>

int main() {
    std::multimap<bool,int> oddness{{false,2}, {true,3}, {true,47}, {false,74656}};
    // nice, does not work:
    // oddness.equal_range(true) | std::views::values;
    // works:
    oddness | std::views::values;

    // working code:
    auto odds = oddness.equal_range(true);
    const auto odds_view = std::views::values(std::ranges::subrange(odds.first, odds.second));
    for (const auto& odd : odds_view) {
        std::cout << odd << std::endl;
    }
}

Upvotes: 1

Views: 610

Answers (1)

Barry
Barry

Reputation: 303107

The problem is that equal_range returns a pair<It, It>, which is (unfortunately) not itself a range. This is one of the more unfortunate legacy API decisions in my opinion - since we have this excellent name but it does something... less than great.

You used to be able to take a pair<It, It> and easily convert it to a subrange, but that's not necessarily valid and was removed (first in LWG3281 and then the rest in LWG3404). Not all pairs of iterators are actually ranges (several algorithms return two iterators that are not - like minmax_element or mismatch).

But that's okay, we can just write our own explicit one:

struct pair_to_range_t {
    template <typename I>
    friend constexpr auto operator|(std::pair<I, I> const& pr, pair_to_range_t) {
        return std::ranges::subrange(pr.first, pr.second);
    }
};

inline constexpr pair_to_range_t pair_to_range{};

And then you can write:

oddness.equal_range(true) | pair_to_range | std::views::values;

Upvotes: 6

Related Questions