title | document | date | audience | author | toc | |||||
---|---|---|---|---|---|---|---|---|---|---|
Support Non-copyable Types for `single_view` |
P2483R0 |
2021-09-30 |
LEWG |
|
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_lines
In 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 parameterT
withmove_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 view
s 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 view
s that store the function objects. Potentially, the @_copyable-box_@
can be removed and replaced by the @_movable-box_@
that is proposed in this paper.