I wanna write a container with random-access-iterator:
#include <cstddef>
#include <iterator>
#include <concepts>
namespace foo
{
struct container
{
struct iter
{
using difference_type = std::ptrdiff_t;
using pointer = int*;
using reference = int&;
using value_type = int;
using iterator_category = ::std::random_access_iterator_tag;
iter& operator ();
iter operator (int);
iter& operator--();
iter operator--(int);
iter operator (difference_type) const;
iter operator-(difference_type) const;
iter& operator =(difference_type);
iter& operator-=(difference_type);
bool operator==(const iter&) const;
bool operator!=(const iter&) const;
bool operator<(const iter&) const;
bool operator>(const iter&) const;
bool operator<=(const iter&) const;
bool operator>=(const iter&) const;
difference_type operator-(const iter&) const;
int& operator*() const;
int& operator[](difference_type) const;
};
// code for reverse iterator, now it is comment out
/* using riter = std::reverse_iterator<iter>;
iter end();
riter rbegin()
{
return std::reverse_iterator(this->end());
}
*/
};
inline container::iter operator (container::iter::difference_type, const container::iter&);
}
int main(void)
{
static_assert(std::random_access_iterator<foo::container::iter>, "");
return 0;
}
Then I compile it with g -11 -std=c 20 and it can be compiled successfully.
But if I want to write a reverse iterator for this container, that is, uncomment those code for reverse iterator, there will be a compile error:
<source>:55:2: error: static_assert failed ""
static_assert(std::random_access_iterator<foo::container::iter>, "");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:55:21: note: because 'foo::container::iter' does not satisfy 'random_access_iterator'
static_assert(std::random_access_iterator<foo::container::iter>, "");
^
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c /11.2.0/bits/iterator_concepts.h:669:8: note: because '__n __j' would be invalid: invalid operands to binary expression ('const iter_difference_t<foo::container::iter>' (aka 'const long') and 'const foo::container::iter')
{ __n __j } -> same_as<_Iter>;
^
1 error generated.
And I also use Clang 13 but got the same result. So how should I write the iterator correctly?
CodePudding user response:
The mystery here is why adding a few lines of code that don't alter the definition of foo::container::iter causes the static_assert to fail.
This is because the implementation of std::reverse_iterator's constructor, in the body of the commented function, evaluates concept std::random_access_iterator to determine what sort of reverse iterator to create.
When this happens, operator (difference_type, iter) hasn't been declared yet because it's further down in your code, so the your container::iter initially fails to satisfy the concept.
Later, you declare the required operator and your type now would satisfy concept std::random_access_iterator, were it not for the following part of the standard: [temp.constr.atomic]
... If, at different points in the program, the satisfaction result is different for identical atomic constraints and template arguments, the program is ill-formed, no diagnostic required.
At different points in your code, the concept std::random_access_iterator has different satisfaction results! So your code is ill-formed, no diagnostic required. (GCC does actually diagnose this - clang just gives you the error message from when it first evaluated the concept, which is why you were confused).
You can fix this by moving your declaration of operator before using std::reverse_iterator. For example:
struct container {
struct iter {
// all your methods, as before
friend iter operator (difference_type, iter);
}
using riter = std::reverse_iterator<iter>;
iter end();
riter rbegin();
}
CodePudding user response:
Just because you give your iterator a random_access_iterator_tag category doesn't make it a random access iterator.
You have to provide all the operations.
The compiler error is telling you that the expression 3 iter_var is invalid (where iter_var is one of your iterators).
The link that Remy provided lists the operations that "have to work".
