Update:
Thanks to everyone who submitted an answer.
In short, the answer is that the "iterators" that begin()
and end()
return must be copyable.
Artyer proposed a nice workaround: Make an iterator class that contains a reference (or, alternatively, a pointer) to the non-copyable object. Below is example code:
struct Element {};
struct Container {
Element element;
struct Iterator {
Container * c;
Iterator ( Container * c ) : c(c) {}
bool operator != ( const Iterator & end ) const { return c != end.c; }
void operator ++ () { c = nullptr; }
const Element & operator * () const { return c->element; }
};
Iterator begin () { return Iterator ( this ); }
Iterator end () { return Iterator ( nullptr ); }
};
#include <stdio.h>
int main () {
Container c;
printf ( "main %p\n", & c .element );
for ( const Element & e : c ) { printf ( "loop %p\n", & e ); }
return 0;
}
Original Question:
The below C++ code will not compile (at least not with g++
version 9.3.0 on Ubuntu 20.04).
The error message is:
use of deleted function 'Iterator::Iterator(const Iterator&)'
Based on the error, am I correct in concluding that the "iterators" returned by begin()
and end()
must be copyable? Or is there some way to use a non-copyable iterator that is returned by reference?
struct Iterator {
Iterator () {}
// I want to prevent the copying of Iterators, so...
Iterator ( const Iterator & other ) = delete;
bool operator != ( const Iterator & other ) { return false; }
Iterator & operator ++ () { return * this; }
Iterator & operator * () { return * this; }
};
struct Container {
Iterator iterator;
Iterator & begin() { return iterator; }
Iterator & end() { return iterator; }
};
int main () {
Container container;
for ( const Iterator & iterator : container ) {}
// The above for loop causes the following compile time error:
// error: use of deleted function 'Iterator::Iterator(const Iterator&)'
return 0;
}
Yes, iterators must be copyable. This is because range iteration is logically equivalent to the following code:
This is the C++11 version. C++17, and later, are slightly different, but the fundamental reason is the same:
__begin
and__end
areauto
, and notauto &
, or something like that. They are a non-reference type. The "begin_expr" and "end_expr", in so many words, are thebegin
andend
expressions that end up calling your custombegin()
andend()
.Even if your
begin()
andend()
returns references, they get assigned to a non-reference type and, as such, must be copyable.Note that even if this was not the case, the shown implementation is not very useful, since both references will always be so the same object, and so the begin and the end expression will end up being the same object, and always compare equal (hopefully).