iter_common_reference_t
does not conform to the definition of indirectly_readable
Section: 24.2 [iterator.synopsis], 24.5.3.2 [const.iterators.alias], 24.5.3.3 [const.iterators.iterator] Status: New Submitter: Hewill Kang Opened: 2023-06-28 Last modified: 2023-10-30
Priority: 3
View all other issues in [iterator.synopsis].
View all issues with New status.
Discussion:
The indirectly_readable
concept (24.3.4.2 [iterator.concept.readable]) requires
iter_reference_t<In>&&
and iter_value_t<In>&
to model
common_reference_with
, which ensures that the input iterator always has a common reference type.
iter_common_reference_t
for computing such types is defined as
common_reference_t<iter_reference_t<T>, indirect-value-t<T>>
.
It is unclear why the formula here drop the &&
part of iter_reference_t<In>
,
but theoretically it is not completely equivalent to the former, for example:
#include <iterator>
struct Ref {
Ref(const Ref&) = delete;
};
struct Val {
operator const Ref&() const &;
};
struct I {
using value_type = Val;
using difference_type = int;
Ref operator*() const;
I& operator++();
I operator++(int);
};
static_assert(std::input_iterator<I>);
using reference = std::iter_reference_t<I>;
using value_type = std::iter_value_t<I>;
static_assert(std::same_as<std::common_reference_t<reference&&, value_type&>, const Ref&>);
static_assert(std::same_as<std::common_reference_t<reference , value_type&>, Ref >);
std::iter_value_t<I> val;
std::iter_common_reference_t<I> cr = val; // failed
In the above example, input_iterator
ensures that the iterator's lvalue value type and rvalue reference type
can be bound to its common reference type, namely const Ref&
, but the type calculated by
iter_common_reference_t
is Ref
, which cannot be bound by both.
&&
to iter_reference_t<In>
in formulas of similar
form to conform to the definition of indirectly_readable
.
[2023-10-30; Reflector poll]
Set priority to 3 after reflector poll.
"NAD - This can easily lead to dangling references. This only matters if
iter_reference_t
isn't a language reference type,
and the change causes common_reference
to produce
a language reference type. So binding to the common reference requires
a temporary. That's not going to work if the type is used as a return type
(as the const-cases are).
As written I think it also causes significant damage to constant-iterator."
Proposed resolution:
This wording is relative to N4950.
Modify 24.2 [iterator.synopsis], header <iterator>
synopsis, as indicated:
[…] template<indirectly_readable T> using iter_common_reference_t = // freestanding common_reference_t<iter_reference_t<T>&&, indirect-value-t<T>>; […]
Modify 24.5.3.2 [const.iterators.alias] as indicated:
template<indirectly_readable It> using iter_const_reference_t = common_reference_t<const iter_value_t<It>&&, iter_reference_t<It>&&>;
Modify 24.5.3.3 [const.iterators.iterator] as indicated:
namespace std { template<class I> concept not-a-const-iterator = see below; // exposition only 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>&&>; […] };