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

ranges::views::cache1 moves from the cache on read. #1766

Open MPJansen opened 1 year ago

MPJansen commented 1 year ago

Hi! Thank you so much for providing this excellent library.

While trying to avoid a known behavior of transform-filter pairs as described here I ran into an issue with views::cache1.

The code looks like

auto pipeline = ranges::views::transform(
   [](auto object) {
     return object;
    }) 
  | ranges::views::cache1
  | ranges::views::filter(
    [](auto object /*const auto& object*/) { <-- A
      return object.has_value(); // <-- B -- object is ok here
   }) //
  | ranges::views::transform(
    [](auto object) {
      // <-- C -- object is moved from/unititialized here
    });

using this pipeline result in an uninitialized object on line C. Removing the cache1 fixes this but result in the first transform being executed twice per element. Turning the argument on line A into a const reference also fixes the problem, but I am unsure why and think it is undesirable.

Inspecting the implementation of cache1 I found the following:

range_value_t<Rng> && read() const
{
    if(parent_->dirty_)
    {
        parent_->update_(*current_);
        parent_->dirty_ = false;
    }
    return std::move(*parent_->cache_);
}

It seems that the read function of cache1's cursor actually moves from the cache, which seemingly defeats the purpose of having one. Therefore if filter has a value-type argument it moves from the cache but leaves the dirty_ flag false. the next read of this iterator then reads from an uninitialized cache.

I think this might be a bug in the implementation, but am unsure.

Kind regards,