C++20 has explicit library support for std::contiguous_iterator_tag
. Some STL algorithms (e.g. std::copy
) can perform much better on contiguous iterators. However, I'm unclear on exactly how the programmer is supposed to get access to this new functionality.
Let's suppose for the sake of argument that we have a completely conforming C++20 library implementation. And I want to write the simplest possible contiguous iterator.
#include <iterator>
class MyIterator {
int *p_;
public:
using value_type = int;
using reference = int&;
using pointer = int*;
using difference_type = int;
using iterator_category = std::contiguous_iterator_tag;
int *operator->() const;
int& operator*() const;
int& operator[](int) const;
MyIterator& operator++();
MyIterator operator++(int);
MyIterator& operator--();
MyIterator operator--(int);
MyIterator& operator+=(int);
MyIterator& operator-=(int);
friend auto operator<=>(MyIterator, MyIterator) = default;
friend int operator-(MyIterator, MyIterator);
friend MyIterator operator+(MyIterator, int);
friend MyIterator operator-(MyIterator, int);
friend MyIterator operator+(int, MyIterator);
};
namespace std {
int *to_address(MyIterator it) {
return it.operator->();
}
}
static_assert(std::contiguous_iterator<MyIterator>); // FAILS
This fails on both GCC/libstdc++ and MSVC/STL; but is it supposed to?
For my next attempt, I specialized pointer_traits<MyIterator>
. MyIterator
isn't actually a pointer, so I didn't put anything in pointer_traits
except the one function that the library needs. This is my second attempt:
#include <iterator>
class MyIterator {
~~~
};
template<>
struct std::pointer_traits<MyIterator> {
int *to_address(MyIterator it) {
return it.operator->();
}
};
static_assert(std::contiguous_iterator<MyIterator>); // OK!
Is this how I'm supposed to do it? It feels extremely hacky. (To be clear: my first failed attempt also feels extremely hacky.)
Am I missing some simpler way?
In particular, is there any way for MyIterator
itself to warrant that it's contiguous, just using members and friends and stuff that can be defined right there in the body of the class? Like, if MyIterator
is defined in some deeply nested namespace, and I don't want to break all the way out to the top namespace in order to open namespace std
.
EDITED TO ADD: Glen Fernandes informs me that there is a simpler way — I should just add an element_type
typedef, like this! (And I can remove 3 of iterator_traits
' big 5 typedefs in C++20.) This is looking nicer!
#include <iterator>
class MyIterator {
int *p_;
public:
using value_type = int;
using element_type = int;
using iterator_category = std::contiguous_iterator_tag;
int *operator->() const;
int& operator*() const;
int& operator[](int) const;
MyIterator& operator++();
MyIterator operator++(int);
MyIterator& operator--();
MyIterator operator--(int);
MyIterator& operator+=(int);
MyIterator& operator-=(int);
friend auto operator<=>(MyIterator, MyIterator) = default;
friend int operator-(MyIterator, MyIterator);
friend MyIterator operator+(MyIterator, int);
friend MyIterator operator-(MyIterator, int);
friend MyIterator operator+(int, MyIterator);
};
static_assert(std::contiguous_iterator<MyIterator>); // OK!
Also, I've seen some stuff about using member typedef iterator_concept
instead of iterator_category
. Why might I ever want to provide MyIterator::iterator_concept
? (I'm not asking for a full historical explanation here; just a simple best-practice guideline "nah forget about iterator_concept
" or "yes provide it because it helps with X" would be nice.)