cpp_iterators
#include <compare>
#include <concepts>
#include <cstddef>
#include <iterator>
#include <type_traits>
template <typename TYPE>
struct Container {
template <typename T>
struct Iterator {
static_assert(std::is_same_v<std::remove_const_t<T>, TYPE>);
using iterator_concept = std::contiguous_iterator_tag; // TODO
using iterator_category = std::random_access_iterator_tag; // TODO
// cast non-const to const
template <typename U>
requires (std::is_const_v<T> &&
std::same_as<std::remove_const_t<T>, U>)
constexpr Iterator(Iterator<U> const& other) noexcept : ptr_(other.ptr_) {} // TODO
// INPUT-OR-OUTPUT: dereference, increment, signed difference type.
using difference_type = std::ptrdiff_t;
constexpr T& operator*() const noexcept {
// TODO
}
constexpr Iterator& operator++() noexcept {
++ptr_; // TODO
return *this;
}
constexpr Iterator operator++(int) noexcept {
auto previous = *this;
++*this;
return previous;
}
// INPUT: adds readable dereference and an associated value type.
using value_type = std::remove_cv_t<T>;
// operator* above already supports reading through a const iterator object.
// For T = int, its return type is int&.
// For T = const int, its return type is const int&.
// No new function is needed here: T& and value_type satisfy indirectly_readable.
// Input alone does not require copying, default construction, or equality.
// OUTPUT: adds writable dereference; independent of the input hierarchy.
// No extra iterator member is needed: assignment acts on the returned T&.
// Required expressions for an appropriate value:
// *it = value;
// *it++ = value;
// These work for Iterator<int>, but not Iterator<const int>.
// Our reference return also satisfies indirectly_writable's other forms.
// FORWARD //
constexpr Iterator() noexcept = default;
// Copy/move construction and copy/move assignment are implicit.
// Postfix ++ above must return Iterator, representing the old position.
constexpr bool operator==(const Iterator&) const noexcept = default;
// BIDIRECTIONAL //
constexpr Iterator& operator--() noexcept {
--ptr_; // TODO
return *this;
}
constexpr Iterator operator--(int) noexcept {
auto previous = *this;
--*this;
return previous;
}
// RANDOM ACCESS //
constexpr Iterator& operator+=(difference_type n) noexcept {
ptr_ += n; // TODO
return *this;
}
constexpr Iterator& operator-=(difference_type n) noexcept {
ptr_ -= n; // TODO
return *this;
}
constexpr Iterator operator+(difference_type n) const noexcept {
auto result = *this;
return result += n;
}
friend constexpr Iterator operator+(difference_type n, Iterator it) noexcept {
return it += n;
}
constexpr Iterator operator-(difference_type n) const noexcept {
auto result = *this;
return result -= n;
}
constexpr difference_type operator-(const Iterator& other) const noexcept {
// TODO
if (ptr_ == other.ptr_) return 0; // Includes two default iterators.
return ptr_ - other.ptr_;
}
constexpr T& operator[](difference_type n) const noexcept {
return ptr_[n]; // Same return type as operator*.
}
constexpr auto operator<=>(const Iterator&) const noexcept = default;
// CONTIGUOUS: adds address extraction and contiguous storage guarantees.
constexpr T* operator->() const noexcept {
return ptr_; // Makes std::to_address(it) work, including at end.
}
// Conventional aliases for iterator_traits / legacy interoperability.
using reference = T&;
using pointer = T*;
private:
T* ptr_ = nullptr; // TODO
template <typename> friend struct Iterator; // Allow mutable -> const access.
};
};