Consider the following class:
template <class T>
struct X {
T& operator*() & { return t; }
T& operator*() && = delete;
X& operator++() { return *this; }
T t;
};
Does this class satisfy requirements of Iterator concept by the C++ Standard? Object of this class is incrementable and dereferenceable. But dereference of rvalue object is forbidden.
int main() {
X<int> x;
*x; // ok
*X<int>(); // fail. But is it really necessary for Iterator by Standard?
}
With a strict reading of the standard; [iterator.iterators]/2 (§24.2.2/2) does hint at the type X
qualifying as an iterator;
...
a
andb
denote values of typeX
orconst X
...
r
denotes a value ofX&
...A type
X
satisfies the Iterator requirements if:
X satisfies the
CopyConstructible
,CopyAssignable
, andDestructible
requirements ([utility.arg.requirements]) and lvalues of typeX
are swappable ([swappable.requirements]), andthe expressions in Table (below) are valid and have the indicated semantics.
*r
(r
is dereferenceable)++r
(returnsX&
)
Given the code;
template <class T>
struct X {
T& operator*() & { return t; }
T& operator*() && = delete;
X& operator++() { return *this; }
T t;
};
int main()
{
X<int> x;
++x;
int i = *x;
X<int> y(x);
using std::swap;
std::swap(x, y);
}
Certainly does seem to satisfy those requirements.
However, the story continues, the Iterator concept, as defined above, is not listed as one of the Iterator Categories in the standard §24.2.1/2;
This International Standard defines five categories of iterators, according to the operations defined on them: input iterators, output iterators, forward iterators, bidirectional iterators and random access iterators...
They all define an operation *a
and *r++
for which the type X
fails to compile;
int j = *x++; // fails to compile even with the inclusion of the post increment operator
const X<int> xc {};
int ic = *x1; // no const overloads
For an iterator to be usable within one of the defined categories, it needs to include further members for de-referencing const
values, lvalues and rvalues, post increment etc. In the spirit of the standard;
Iterators are a generalization of pointers that allow a C++ program to work with different data structures (containers) in a uniform manner.
The guidance here for the overloaded members and operators is that they can/should be added to enforce compliance and optimise (if possible) the implementation of the semantics of the generalised pointer - not to disallow the semantics; limiting the semantics could have unintended consequences.