range-v3
range-v3 copied to clipboard
std::array | views::for_each | to_vector invokes lambda twice on each element
It doesn't do this for std::vector
, or if we change for_each
to transform
. Annoying bug to troubleshoot if your invokable is not regular.
Heres a mcve https://godbolt.org/z/b5bv9fvrj
Annoying bug to troubleshoot if your invokable is not regular.
Regular is a requirement on the invocable for transform
and for_each
.
The specific reason for the change in behavior from array
to vector
:
auto in = std::array{1, 2, 3};
auto out = in | views::transform([](int i){ return std::vector{i}; } | views::join;
out
here is actually still a sized_range
in range-v3 (whereas it would not be if in
were a vector<int>
). The reason that out
is still sized is that join
provides a size
for known-fixed-size ranges (range_cardinality<vector<int>>
is finite
but range_cardinality<array<int, 2>>
is 2
):
https://github.com/ericniebler/range-v3/blob/3d6e6f56e5e1a3ec4befcc7695504ea23e1d52ab/include/range/v3/view/join.hpp#L160-L172
When you then pipe that into to_vector
, for sized ranges we use the size to allocate the correct amount - which in this case requires walking the underlying range (the transform_view
) which requires invoking the function.
Regular is a requirement on the invocable for
transform
andfor_each
.
Any observable behaviour will be depended upon ;).
Perhaps this line https://github.com/ericniebler/range-v3/blob/3d6e6f56e5e1a3ec4befcc7695504ea23e1d52ab/include/range/v3/view/join.hpp#L166 might be changed to
(range_cardinality<range_reference_t<Rng>>::value >= 0))
or added after.
Regular is a requirement on the invocable for
transform
andfor_each
.Any observable behaviour will be depended upon ;).
This isn't a Hyrum's Law thing, you're simply violating the preconditions of the algorithm.