Reputation: 16242
I have coded an iterators-like class and for some reason it doesn’t pass the Readable concept as defined in Range v3. I don’t know why and I am trying to see exactly how I need to modify the syntax (and semantics) to fulfill the concept.
What are the minimum syntactic requirements for an iterator to be Readable according to Range v3? Can that be written a set of statements-that-must-compile? (see example below)
I have an iterator It
with which I can do the basic stuff (what I would call "readable"), yet it doesn't pass the concept check:
#include <range/v3/all.hpp>
...
It i; // ok
typename It::value_type val = *i; // ok
typename It::reference ref = *i; // ok
typename It::value_type val2{ref}; // ok
static_assert( ranges::CommonReference<typename It::reference&&, typename It::value_type&>{} ); // ok
static_assert( ranges::Readable<It>{} ); // error: static assertion failed
What other constructs involving i
I can write that it will make obvious that It
is not Readable? In other words, what generic code would compile only and only-if the iterator is Range v3-Readable?
In many places, it says "if it behaves like a pointer then is Readable", but I can not find what is wrong with my iterator. I will be able to understand what is wrong when I see what code needs to compile?
I am trying to debug why my iterator fails to fullfil the concept (and therefore is rejected by range v3 functions). Note that is It
were std::vector<bool>::iterator
it will all work.
The Readable concept code in Range v3 https://ericniebler.github.io/range-v3/structranges_1_1v3_1_1concepts_1_1_readable.html is similar to https://en.cppreference.com/w/cpp/experimental/ranges/iterator/Readable
(I am using version 0.5.0 [Fedora30])
template < class In >
concept bool Readable =
requires {
typename ranges::value_type_t<In>;
typename ranges::reference_t<In>;
typename ranges::rvalue_reference_t<In>;
} &&
CommonReference<ranges::reference_t<In>&&, ranges::value_type_t<In>&> &&
CommonReference<ranges::reference_t<In>&&, ranges::rvalue_reference_t<In>&&> &&
CommonReference<ranges::rvalue_reference_t<In>&&, const ranges::value_type_t<In>&>;
So it looks like the iterator must (or can deduce) value_t<It>
, extracted from It::value_type
, reference_t<It>
extracted from It::reference
.
I don't know how rvalue_reference_t
is deduced or what CommonReference
means in terms of contrains to the syntax.
Upvotes: 2
Views: 367
Reputation: 6731
For an iterator to model Readable
in range-v3, it needs:
operator *
readable_traits
or have a public member type value_type
or element_type
defining the associated value type.The simplest Readable
user-defined type I can think of is:
#include <range/v3/all.hpp>
template <typename T>
class It
{
public:
using value_type = T;
private:
T x;
public:
T operator *() const { return x; }
};
static_assert( ranges::Readable<It<int>>{} );
which compiles cleanly with range-v3 version 0.3.5 (https://godbolt.org/z/JMkODj).
In version 1 of range-v3 Readable
is no longer a type, but a constexpr
value convertible to bool
, so that the correct assertion in this case would be:
static_assert( ranges::Readable<It<int>> );
value_type
and the value returned by operator *
need not to be the same. However, they must be in a sense inter-convertible for the algorithms to work. That's where the CommonReference
concept comes into play. This concept basically requires that two types share a “common reference type” to which both can be converted. It essentially delegates to a common_reference
type traits, whose behaviour is described in great detail at cppreference (note, however, that what is described there is for the stuff in the ranges TS, which may not be exactly the same as that of the range-v3 library).
In practice, the Readable
concept can be satisfied by defining a conversion operator in the type returned by operator *. Here's a simple example that passes the test (https://godbolt.org/z/5KkNpv):
#include <range/v3/all.hpp>
template <typename T>
class It
{
private:
class Proxy
{
private:
T &x;
public:
Proxy(T &x_) : x(x_) {}
operator T &() const { return x; }
};
public:
using value_type = T;
private:
T x;
public:
Proxy operator *() { return {x}; }
};
static_assert( ranges::Readable<It<bool>>{} );
Upvotes: 3