basic_const_iterator
should have custom iter_move
Section: 24.5.3 [const.iterators] Status: C++23 Submitter: Hewill Kang Opened: 2023-01-31 Last modified: 2023-11-22
Priority: 3
View other active issues in [const.iterators].
View all other issues in [const.iterators].
View all issues with C++23 status.
Discussion:
The standard does not currently customize iter_move
for basic_const_iterator
,
which means that applying iter_move
to basic_const_iterator
will invoke the default behavior.
Although the intent of such an operation is unpredictable, it does introduce some inconsistencies:
int x[] = {1, 2, 3};
using R1 = decltype( x | views::as_rvalue | views::as_const);
using R2 = decltype( x | views::as_const | views::as_rvalue);
using Z1 = decltype(views::zip(x) | views::as_rvalue | views::as_const);
using Z2 = decltype(views::zip(x) | views::as_const | views::as_rvalue);
static_assert(same_as<ranges::range_reference_t<R1>, const int&&>);
static_assert(same_as<ranges::range_reference_t<R2>, const int&&>);
static_assert(same_as<ranges::range_reference_t<Z1>, tuple<const int&&>>);
static_assert(same_as<ranges::range_reference_t<Z2>, tuple<const int&&>>); // failed
In the above example, views::zip(x) | views::as_const
will produce a range whose iterator type is
basic_const_iterator
with reference of tuple<const int&>
.
Since iter_move
adopts the default behavior, its rvalue reference will also be
tuple<const int&>
, so applying views::as_rvalue
to it won't have any effect.
iter_move
specialization for basic_const_iterator
and specifies
the return type as
common_reference_t<const iter_value_t<It>&&, iter_rvalue_reference_t<It>>
,
which is the type that input_iterator
is guaranteed to be valid. This is also in sync with the behavior
of range-v3.
[Issaquah 2023-02-10; LWG issue processing]
Move to Immediate for C++23
[2023-02-13 Approved at February 2023 meeting in Issaquah. Status changed: Immediate → WP.]
Proposed resolution:
This wording is relative to N4928.
Modify 24.5.3 [const.iterators] as indicated:
namespace std { template<class I> concept not-a-const-iterator = see below; template<indirectly_readable I> using iter-const-rvalue-reference-t = // exposition only common_reference_t<const iter_value_t<I>&&, iter_rvalue_reference_t<I>>; template<input_iterator Iterator> class basic_const_iterator { Iterator current_ = Iterator(); // exposition only using reference = iter_const_reference_t<Iterator>; // exposition only using rvalue-reference = iter-const-rvalue-reference-t<Iterator>; // exposition only public: […] template<sized_sentinel_for<Iterator> S> requires different-from<S, basic_const_iterator> friend constexpr difference_type operator-(const S& x, const basic_const_iterator& y); friend constexpr rvalue-reference iter_move(const basic_const_iterator& i) noexcept(noexcept(static_cast<rvalue-reference>(ranges::iter_move(i.current_)))) { return static_cast<rvalue-reference>(ranges::iter_move(i.current_)); } }; }