Reputation: 25643
I start to play with std::ranges
and want understand how views really work. So I try to write my own container and iterator type and want to use it in a view.
But something seems to be missing but the compiler only tells me that there is no begin()
method inside the view but not why.
Example:
#include <iostream>
#include <array>
#include <ranges>
class MyFixedContainer;
class MyIterator
{
MyFixedContainer* ptr;
unsigned int offset;
public:
MyIterator( MyFixedContainer* ptr_, unsigned int offset_ ): ptr{ ptr_},offset{offset_}{}
bool operator==( MyIterator& other ) const
{
return ( ptr == other.ptr )&& ( offset == other.offset );
}
bool operator!=( MyIterator& other ) const
{
return !(*this == other);
}
MyIterator operator++()
{
offset++;
return *this;
}
MyIterator operator++(int)
{
MyIterator tmp = *this;
offset++;
return tmp;
}
int operator*() const;
};
class MyFixedContainer
{
std::array<int,4> arr={5,6,7,8};
public:
auto begin() { return MyIterator{ this, 0 }; }
auto end() { return MyIterator{ this, 4}; }
int Get( int offset ) const
{
return arr[ offset ];
}
};
int MyIterator::operator*() const
{
return ptr->Get( offset );
}
int main()
{
MyFixedContainer c;
// Container type itself works:
for ( int i: c )
{
std::cout << i << std::endl;
}
// Try to use with std::ranges
auto even = [] (int i) { return 0 == i % 2; };
auto y = std::views::filter(c, even);
auto b = y.begin(); // << error message
}
Compiles with
main.cpp:90:16: error: 'struct std::ranges::views::__adaptor::_RangeAdaptorClosurestd::ranges::views::__adaptor::_RangeAdaptor<_Callable::operator()<{MyFixedContainer&, main()::<lambda(int)>&}>::<lambda(_Range&&)> >' has no member named 'begin' 90 | auto b = y.begin();
Upvotes: 8
Views: 1029
Reputation: 6751
MyIterator
does not model std::input_or_output_iterator
because:
std::iter_difference_t<MyIterator>
must be valid, andMyIterator
is not a std::sentinel_for
<MyIterator, MyIterator>
because its operators ==
and !=
take references instead of const
references.
MyIterator
does not satisfy std::input_iterator
, which requires std::iter_value_t
to be valid.
Fixing all of the above:
#include <iostream>
#include <array>
#include <ranges>
class MyFixedContainer;
class MyIterator
{
MyFixedContainer* ptr;
unsigned int offset;
public:
using difference_type = int;
using value_type = int;
MyIterator() = default;
MyIterator( MyFixedContainer* ptr_, unsigned int offset_ ): ptr{ ptr_},offset{offset_}{}
bool operator==( MyIterator const & other ) const
{
return ( ptr == other.ptr )&& ( offset == other.offset );
}
bool operator!=( MyIterator const & other ) const
{
return !(*this == other);
}
MyIterator &operator++()
{
offset++;
return *this;
}
MyIterator operator++(int)
{
MyIterator tmp = *this;
offset++;
return tmp;
}
int operator*() const;
};
class MyFixedContainer
{
std::array<int,4> arr={5,6,7,8};
public:
auto begin() { return MyIterator{ this, 0 }; }
auto end() { return MyIterator{ this, 4}; }
int Get( int offset ) const
{
return arr[ offset ];
}
};
int MyIterator::operator*() const
{
return ptr->Get( offset );
}
int main()
{
MyFixedContainer c;
// Container type itself works:
for ( int i: c )
{
std::cout << i << std::endl;
}
// Try to use with std::ranges
auto even = [] (int i) { return 0 == i % 2; };
static_assert(std::input_or_output_iterator<MyIterator>);
static_assert(std::ranges::input_range<MyFixedContainer>);
auto y = c | std::views::filter(even);
auto b = y.begin(); // << OK
}
The error messages are much clearer if you static_assert
every concept that your container/iterator has to model.
Upvotes: 9