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

康桓瑋*_*康桓瑋 10 c++ c++20 std-ranges c++23

一些范围适配器,例如filter_\xc2\xadview,take_\xc2\xadwhile_\xc2\xadviewtransform_view使用std::optional\ 的同类copyable-box来存储可调用对象:

\n
template<input_\xc2\xadrange V, copy_\xc2\xadconstructible F>\nclass transform_view : public view_interface<transform_view<V, F>> {\n private:\n  V base_ = V();\n  copyable-box<F> fun_;\n};\n
Run Code Online (Sandbox Code Playgroud)\n

这要求可调用对象Fcopy_\xc2\xadconstructible,这也阻止我们将捕获仅移动对象的可调用对象传递给transform_viewGodbolt):

\n
#include <ranges>\n#include <memory>\n\nstruct MoveOnlyFun {\n  std::unique_ptr<int> x;\n  MoveOnlyFun(int x) : x(std::make_unique<int>(x)) { } \n  int operator()(int y) const { return *x + y; }\n};\n\nint main() {\n  auto r = std::views::iota(0, 5)\n         | std::views::transform(MoveOnlyFun(1));\n}\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

更新:

\n

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

\n

T.C*_*.C. 8

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

从历史上看,当添加这些适配器时,视图必须是copyable,因此我们要求函数对象是copy_constructible(我们不能copyable在不排除捕获式 lambda 的情况下要求)。view后来才进行了仅需要的更改movable

放宽限制可能是可能的,但需要一份文件,而且并不是真正的优先事项。

  • [它不是](https://timsong-cpp.github.io/cppwp/algorithms.requirements#10),但实现可以选择不检查,如果特定算法的实现碰巧没有复制,那么你可以得到可以这么说,摆脱它。 (3认同)