Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

In C++ std::ranges, how do I build a map out of a views::join result?

I’m using GCC 14 in C+23 mode. In the following code, I create a view of views of pairs, which I then flatten with views::join and put into a vector:

auto c = std::ranges::views::iota(1, 5)
        | std::ranges::views::transform([](int const v){
                return std::ranges::views::iota(1, 3)
                    | std::ranges::views::transform([v](int const w){
                            return std::make_pair(v, std::format("[{}/{}]", v, w));
                    });
        })
        | std::ranges::views::join
        | std::ranges::to<std::vector>();

The result is a vector of pairs.

Now, instead of a vector of pairs, I’d like to create a map (or unordered_map). Constructing a map from a range of pairs should work (as I’ve learned in my old question). But here this doesn’t compile:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

auto c = std::ranges::views::iota(1, 5)
        | std::ranges::views::transform([](int const v){
                return std::ranges::views::iota(1, 3)
                    | std::ranges::views::transform([v](int const w){
                            return std::make_pair(v, std::format("[{}/{}]", v, w));
                    });
        })
        | std::ranges::views::join
        | std::ranges::to<std::map>();

Unfortunately, the compiler output isn’t really understandable to me. All I got from it is that it cannot call std::construct_at to construct a map node, but I can’t really grasp much from all the standard library internals.

Using only one level of "nested" ranges works as expected. So this issue occurs only when nested range levels are joined and converted to a map.

How to make the second code to compile and work as expected?

>Solution :

libstdc++ currently does not implement the range version of map‘s constructor, namely map(std::from_range_t, R&&), and since the joined range you construct is not a common_range, ranges::to will dispatch the (2.1.4) bullet to first default-construct a map, and then emplace the elements into the map via c.emplace(c.end(), *it).

Note that it is mostly expected to call emplace() of a sequence container such as vector rather than map since the latter’s emplace() does not need to accept an iterator as the first argument. However, since map::emplace() is an unconstrained function, it is still called and causes a hard error.

The workaround is to use views::common to convert the joined range into a common_range, so the map can be constructed using the classic map(InputIt first, InputIt last) constructor.

auto c = std::views::iota(1, 5)
        | std::views::transform([](int const v){
                return std::views::iota(1, 3)
                    | std::views::transform([v](int const w){
                            return std::make_pair(v, std::format("[{}/{}]", v, w));
                    });
        })
        | std::views::join
        | std::views::common // no need for libc++
        | std::ranges::to<std::map>();

Demo

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading