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
Iis 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_));whereproxyis 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_); } };