Reputation: 1270
I cannot get my user defined containers to work with std::ranges.
My container works if the iterator is simply an int*
but as soon as I make my own iterator class I get compiler errors.
This works.
#include <iostream>
#include <ranges>
using namespace std;
struct sentinel {};
class my_iota_view_a : public std::ranges::view_interface< my_iota_view_a >{
public:
using value_type = size_t;
using iterator = value_type *;
my_iota_view_a() = default;
my_iota_view_a( size_t begin ) : mBegin( &mData[begin] ) { }
iterator begin() { return mBegin; }
sentinel end() { return sentinel{}; }
private:
value_type mData[10] ={ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
value_type * mBegin;
};
bool operator==( my_iota_view_a::iterator lhs, sentinel rhs ) { return false; }
bool operator!=( my_iota_view_a::iterator lhs, sentinel rhs ) { return true; }
int main() {
for( auto i : my_iota_view_a( 1 ) | ranges::views::take(3) )
cout << i << ' ';
cout << "\n";
}
With my own iterator it does not work.
#include <iostream>
#include <ranges>
using namespace std;
struct sentinel {};
class my_iota_view_b : public std::ranges::view_interface< my_iota_view_b > {
public:
struct iterator_type {
using iterator_category = std::output_iterator_tag;
using value_type = size_t;
using reference = value_type; // Intentional not 'value_type &' because
// this iterator modifies the value.
using pointer = value_type*;
using difference_type = std::ptrdiff_t;
iterator_type() = default;
iterator_type( value_type val ) : mVal( val ) {}
iterator_type( const iterator_type & o ) : mVal(o.mVal) {}
iterator_type( iterator_type && o ) noexcept : mVal( std::move( o.mVal ) ) {}
iterator_type operator++( int ) /* Postfix a++ */ { auto it = *this; ++it; return it; }
iterator_type operator++() /* Prefix ++a */{ mVal++; return *this; }
reference operator*() { return mVal; }
value_type mVal{};
};
struct sentinel {};
friend bool operator==( iterator_type lhs, sentinel rhs ) { return lhs.mVal > 1000; }
friend bool operator!=( iterator_type lhs, sentinel rhs ) { return !(lhs == rhs); }
using value_type = size_t;
using iterator = iterator_type;
using const_iterator = iterator_type;
my_iota_view_b() = default;
my_iota_view_b( value_type begin ) : mBegin( begin ) {}
iterator begin() { return mBegin; }
sentinel end() { return sentinel{}; }
size_t size() { return 10; }
private:
value_type mBegin{};
};
bool operator==( my_iota_view_b::iterator lhs, sentinel rhs ) { return false; }
bool operator!=( my_iota_view_b::iterator lhs, sentinel rhs ) { return true; }
int main() {
for( auto i : my_iota_view_b( 1 ) | ranges::views::take(3) ) // *1
cout << i << ' ';
for( auto i : my_iota_view_b( 1 ) ) // An old school range based for works.
cout << i << ' ';
}
// Line *1 error: no match for 'operator|' (operand types are 'my_iota_view_b' and ranges::views::take(3)
Is there something new that has to be done for std::ranges
in order to make this work?
I am compiling with Visual Studio 16.8.4 (latest version as of writing), and the compile error is
error C2678: binary '|': no operator found which takes a left-hand operand of type 'my_iota_view_b' (or there is no acceptable conversion)
But GCC gives the same error.
I have found examples of how to make range views, but none that show me how to make my own iterator.
Upvotes: 2
Views: 525
Reputation: 303057
The problem is that your iterator is not an iterator. The best way to answer this question is to use a static assertion:
static_assert(std::input_iterator<my_iota_view_b::iterator>);
If you do that, you'll see all the things your iterator is failing to satisfy. The first one is that it's not move-assignable. This is because you provided a move constructor - which you should just not do and let the compiler generate them for you.
Once you delete your move and copy constructors, you'll see the next error, which is the prefix increment doesn't return a reference and it should.
And then that operator*()
isn't const
, and it needs to be.
And then that your iterator_category
is output_iterator_tag
when it should be forward_iterator_tag
(or even absent entirely).
Once you fix all of those issues, then everything works as expected.
Upvotes: 8