ericniebler / range-v3

Range library for C++14/17/20, basis for C++20's std::ranges
Other
4.06k stars 437 forks source link

std::array | views::for_each | to_vector invokes lambda twice on each element #1728

Open tom-huntington opened 1 year ago

tom-huntington commented 1 year ago

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

brevzin commented 1 year ago

Annoying bug to troubleshoot if your invokable is not regular.

Regular is a requirement on the invocable for transform and for_each.

brevzin commented 1 year ago

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.

tom-huntington commented 1 year ago

Regular is a requirement on the invocable for transform and for_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.

brevzin commented 1 year ago

Regular is a requirement on the invocable for transform and for_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.