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:
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?
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>();