10

Some ranges adaptors such as filter_­view, take_­while_­view and transform_view use std::optional's cousin copyable-box to store the callable object:

template<input_­range V, copy_­constructible F>
class transform_view : public view_interface<transform_view<V, F>> {
 private:
  V base_ = V();
  copyable-box<F> fun_;
};

which requires the callable F to be copy_­constructible, this also prevents us from passing in the callable that captures the move only object into transform_view (Godbolt):

#include <ranges>
#include <memory>

struct MoveOnlyFun {
  std::unique_ptr<int> x;
  MoveOnlyFun(int x) : x(std::make_unique<int>(x)) { } 
  int operator()(int y) const { return *x + y; }
};

int main() {
  auto r = std::views::iota(0, 5)
         | std::views::transform(MoveOnlyFun(1));
}

Since the view is not required to be copy_constructible, why do we require the callable to be copy_constructible? why don't we just use moveable-box to store callable instead of copyable-box? What are the considerations behind this?

Update:

The recent proposal P2494R0 also addresses this issue and proposes a detailed resolution.

4

1 回答 1

8

All the algorithms require copy-constructible function objects, and views are basically lazy algorithms.

Historically, when these adaptors were added, views were required to be copyable, so we required the function objects to be copy_constructible (we couldn't require copyable without ruling out captureful lambdas). The change to make view only require movable came later.

It is probably possible to relax the restriction, but it will need a paper and isn't really high priority.

于 2021-10-09T14:20:02.520 回答