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.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
  • 1
    Note: You are citing a draft standard of C++23 to talk about a C++20 issue. As the time between C++20's standardization and now passes, that draft will become more and more unlike C++20. In this case "copyable-box" is not [even a *thing* in C++20](https://timsong-cpp.github.io/cppwp/n4861/). It uses [`semiregular-box`](https://timsong-cpp.github.io/cppwp/n4861/range.semi.wrap). – Nicol Bolas Oct 09 '21 at 13:36

1 Answers1

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.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • "*All the algorithms require copy-constructible function objects.*" Any particular reasons? – 康桓瑋 Oct 09 '21 at 14:29
  • I'm not sure there's a documented rationale. Allowing algorithms to easily call other algorithms freely could be one (allowing move-constructible would require pass-by-reference if the caller still needs the function object afterwards, and the extra level of indirection isn't great if the function object has little or no state). – T.C. Oct 09 '21 at 15:24
  • Maybe. But since `std::` algorithms can accept move-only functions, and if the standard does not explicitly prohibit this, perhaps we can relax the restrictions on `ranges::` algorithms. – 康桓瑋 Oct 09 '21 at 15:48
  • "`std::` algorithms can accept move-only functions" - citation needed – T.C. Oct 09 '21 at 15:49
  • Is [this](https://godbolt.org/z/ebrKaWxE6) allowed? Although it looks fine, I don't know if the standard prohibits this. – 康桓瑋 Oct 09 '21 at 15:54
  • 3
    [It isn't](https://timsong-cpp.github.io/cppwp/algorithms.requirements#10), but implementations can choose not to check, and if a particular algorithm's implementation happens to not copy then you can get away with it, so to speak. – T.C. Oct 09 '21 at 15:58