common_iterator::operator->()
should return by valueSection: 24.5.5.4 [common.iter.access] Status: C++23 Submitter: Jonathan Wakely Opened: 2022-02-12 Last modified: 2023-11-22
Priority: Not Prioritized
View all other issues in [common.iter.access].
View all issues with C++23 status.
Discussion:
24.5.5.4 [common.iter.access] p5 (5.1) says that common_iterator<T*, S>::operator->()
returns std::get<T*>(v_)
which has type T* const&
. That means that
iterator_traits::pointer
is T* const&
as well (this was recently clarified by LWG
3660). We have an actual pointer here, why are we returning it by reference?
decltype(auto)
is equivalent to auto
. For the first bullet, it would make a lot
more sense for raw pointers to be returned by value. That leaves the case where the iterator has an
operator->()
member, which could potentially benefit from returning by reference. But it must
return something that is iterator-like or pointer-like, which we usually just pass by value. Casey suggested
we should just change common_iterator<I, S>::operator->()
to return by value in all cases.
Libstdc++ has always returned by value, as an unintended consequence of using a union instead of
std::variant<I, S>
, so that it doesn't use std::get<I>
to return the member.
[2022-03-04; Reflector poll]
Set status to Tentatively Ready after six votes in favour during reflector poll.
[2022-07-15; LWG telecon: move to Ready]
[2022-07-25 Approved at July 2022 virtual plenary. Status changed: Ready → WP.]
Proposed resolution:
This wording is relative to N4901.
Modify 24.5.5.1 [common.iterator], class template common_iterator
synopsis, as indicated:
[…] constexpr decltype(auto) operator*(); constexpr decltype(auto) operator*() const requires dereferenceable<const I>; constexprdecltype(auto)operator->() const requires see below; […]
Modify 24.5.5.4 [common.iter.access] as indicated:
constexprdecltype(auto)operator->() const requires see below;-3- The expression in the requires-clause is equivalent to: […]
-4- Preconditions:holds_alternative<I>(v_)
istrue
. -5- Effects:
(5.1) — If
I
is a pointer type or if the expressionget<I>(v_).operator->()
is well-formed, equivalent to:return get<I>(v_);
(5.2) — Otherwise, if
iter_reference_t<I>
is a reference type, equivalent to:auto&& tmp = *get<I> (v_); return addressof(tmp);(5.3) — Otherwise, equivalent to:
return proxy(*get<I>(v_));
whereproxy
is the exposition-only class:class proxy { iter_value_t<I> keep_; constexpr proxy(iter_reference_t<I>&& x) : keep_(std::move(x)) {} public: constexpr const iter_value_t<I>* operator->() const noexcept { return addressof(keep_); } };