为什么 C++23 范围适配器要求可调用对象是可复制构造的?

Why does the C++23 ranges adaptor require a callable object to be copy_­constructible?

一些范围适配器,例如filter_­view, take_­while_­view and transform_view use std::optional's cousin copyable-box来存储可调用对象:

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_;
};

要求可调用 Fcopy_­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));
}

既然 view 不需要是 copy_constructible,为什么我们要求可调用对象是 copy_constructible?为什么我们不直接使用 moveable-box 来存储可调用对象而不是 copyable-box?这背后的考虑是什么?

更新:

最近的提议 P2494R0 也解决了这个问题并提出了详细的解决方案。

所有的算法都需要可复制构造的函数对象,视图基本上是惰性算法。

从历史上看,当添加这些适配器时,视图需要 copyable,因此我们要求函数对象为 copy_constructible(我们不能要求 copyable 没有裁定出捕获的 lambdas)。使 view 只需要 movable 的更改稍后出现。

放宽限制是有可能的,但是需要论文,优先级不是很高。