| title | Support Non-copyable Types for `single_view` | |||||
|---|---|---|---|---|---|---|
| document | P2483R0 | |||||
| date | 2021-09-30 | |||||
| audience | LEWG | |||||
| author |
|
|||||
| toc | false |
This paper proposes allowing single_view to hold non-copyable types.
Currently, single_view requires the object that it holds to satisfy std::copy_constructible concept. This makes the single_view not usable with move only types. For example, the following code is invalid
// foo is move only type
foo make_foo();
std::views::single(make_foo()) // | more_pipe_linesIn the original c++ 20 ranges proposal, the view concept requires semiregular, thus all view implementations have to be copyable. However, after [@P1456R1], view no long requires implementations to be copyable but only movable, so technically the std::copy_constructible constraint can be relaxed to std::move_constructible for the types that single_view holds.
::: cmptable
// foo is move only type
foo make_foo();
std::views::single(std::make_shared<foo>(make_foo()))
| std::views::transform([](const auto& f) -> decltype(auto) {
return (*f);
})
| // more_pipe_lines// foo is move only type
foo make_foo();
std::views::single(make_foo()) // | more_pipe_lines:::
This proposal is a pure library extension.
Modify [ranges.syn]{.sref}
// [range.single], single view
template<@[`copy_constructible`]{.rm}[`move_constructible`]{.add}@ T>
requires is_object_v<T>
class single_view;Modify [range.single.view]{.sref}
namespace std::ranges {
template<@[`copy_constructible`]{.rm}[`move_constructible`]{.add}@ T>
requires is_object_v<T>
class single_view : public view_interface<single_view<T>> {
private:
@[_copyable-box_]{.rm}[_movable-box_]{.add}@<T> value_; // exposition only (see [range.@[copy]{.rm}[move]{.add}@.wrap])
public:
single_view() requires default_initializable<T> = default;
constexpr explicit single_view(const T& t) @[`requires copy_constructible<T>`]{.add}@;
constexpr explicit single_view(T&& t);constexpr explicit single_view(const T& t) @[`requires copy_constructible<T>`]{.add}@;
[1]{.pnum} Effects: Initializes value_ with t.
Add a new section [range.move.wrap] under [range.adaptors]{.sref}
::: add Movable wrapper
[1]{.pnum} @_movable-box<T>_@ behaves exactly like optional<T> with the following differences:
-
[1.1]{.pnum}
@_movable-box<T>_@constrains its type parameterTwithmove_constructible<T> && is_object_v<T> -
[1.2]{.pnum} The default constructor of
@_movable-box<T>_@is equivalent to:
constexpr @_movable-box_@() noexcept(is_nothrow_default_constructible_v<T>) requires default_initializable<T> : @_movable-box_@(){in_place} {}
- [1.3]{.pnum} If
movable<T>is not modelled, the move assignment operator is equivalent to:
@_movable-box_@& operator=(@_movable-box_@&& that) noexcept(is_nothrow_move_constructible_v<T>) { if (this != addressof(that)) { if (that) emplace(std::move(*that)); else reset(); } return *this; }
[2]{.pnum} Recommended practices: @_movable-box_@ should store only a T if either T models movable or is_nothrow_move_constructible_v<T> is true.
:::
An alternative approach is to not have the @_movable-box_@ wrapper. Instead, we can just constrain single_view to only accept movable T. However, this is inconsistent with the rest of the range library.
@_copyable-box_@ is not only used in single_view, but also used in lots of views that hold a function object. For example, transform_view stores the function inside @_copyable-box_@<F>. I think as view is no long required to be copyable, there should be no constraint for the function object F being copyable. And in fact, transform_view stores a base view: V base_. If base_ is not copyable, there is no point to have @_copyable-box_@<F>. This is the case for all other views that store the function objects. Potentially, the @_copyable-box_@ can be removed and replaced by the @_movable-box_@ that is proposed in this paper.