Why can std::move_iterator advertise itself as a forward (or stronger) iterator, when it dereferences to an rvalue reference?

Viewed 116

According to cppreference, std::move_iterator sets its ::iterator_category to the category of its underlying iterator1.

But I reckon it can be an input/output iterator at best, since for forward iterators reference must be an lvalue reference, while move_iterator sets reference (and the return type of operator*) to an rvalue reference2.

Is this a blatant mistagging of the iterator with a wrong category?

Being able to do this for my own iterators is undoubtedly convenient. Is there any reason I shouldn't do this, if even the standard library does so?


1 But anything stronger than random_access_iterator_tag is truncated to random_access_iterator_tag, which is weird, since contiguous_iterator_tag is only supposed to be used for ::iterator_concept.

2 Or leaves it untouched if it's not a reference, but then the underlying iterator shouldn't advertise itself as a forward iterator either.

1 Answers

On one hand, the cppreference article on forward iterator requirements was wrong (already fixed by someone). reference must be any reference (& or &&), not specifically lvalue reference (&). Meaning move_iterator does conform.

But on the other hand, auto-determining ::iterator_category uses different wording, which only permits lvalue references:

concept cpp17-forward-iterator = ... && is_lvalue_reference_v<iter_reference_t<I>>

This looks like a standard defect. I've wrote to lwgchair@gmail.com, waiting for a response.


Sources for forward iterators being able to use any kind of reference:

[forward.iterators]/1.3:

if X is a mutable iterator, reference is a reference to T; if X is a constant iterator, reference is a reference to const T

See also LWG1211 (from 2009), which raised the same issue, and was resolved by N3066 (in 2010), which changed the wording from "lvalue reference" to "any reference". (Thanks @康桓瑋 for the links).

Related