Что именно представляет собой концепция Readable в Range v3? - PullRequest
2 голосов
/ 28 июня 2019

Я кодировал класс, подобный итераторам, и по какой-то причине он не соответствует концепции Readable, как определено в Range v3. Я не знаю почему, и я пытаюсь понять, как именно мне нужно изменить синтаксис (и семантику), чтобы выполнить концепцию.

Какие минимальные синтаксические требования для итератора должны быть читаемыми в соответствии с Range v3? Можно ли написать набор утверждений, которые должны компилироваться? (см. пример ниже)

У меня есть итератор It, с помощью которого я могу делать базовые вещи (которые я бы назвал «читабельными»), но он не проходит проверку концепции:

#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

Какие еще конструкции, включающие i Я могу написать, что станет очевидным, что It не доступен для чтения? Другими словами, какой общий код будет компилироваться только и только-если итератор Range v3-Readable?

Во многих местах написано «если он ведет себя как указатель, значит, доступен для чтения», но я не могу найти, что не так с моим итератором. Я смогу понять, что не так, когда увижу, какой код должен компилироваться?

Я пытаюсь отладить, почему мой итератор не может полностью реализовать концепцию (и поэтому отклоняется функциями range v3). Обратите внимание, что It были std::vector<bool>::iterator, все будет работать.


Читаемый код концепции в диапазоне v3 https://ericniebler.github.io/range-v3/structranges_1_1v3_1_1concepts_1_1_readable.html аналогичен https://en.cppreference.com/w/cpp/experimental/ranges/iterator/Readable

(я использую версию 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>&>;

Похоже, что итератор должен (или может вывести) value_t<It>, извлеченный из It::value_type, reference_t<It>, извлеченный из It::reference.

Я не знаю, как rvalue_reference_t выводится или что CommonReference означает с точки зрения противоречий синтаксису.

1 Ответ

2 голосов
/ 28 июня 2019

Для итератора для моделирования Readable в range-v3 необходимо:

  • для разыменования через значимое operator *
  • чтобы иметь специализацию readable_traits или открытый тип члена value_type или element_type, определяющий связанный тип значения.

Самый простой Readable пользовательский тип, который я могу себе представить, это:

#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>>{} );

, который корректно компилируется с range-v3 версии 0.3.5 (https://godbolt.org/z/JMkODj).

В версии 1 range-v3 Readable больше не является типом, а constexpr значением, конвертируемым в bool, так что правильное утверждение в этом случае будет:

static_assert( ranges::Readable<It<int>> );

value_type и значение, возвращаемое operator *, не обязательно должно быть одинаковым. Тем не менее, они должны быть в некотором смысле взаимозаменяемыми, чтобы алгоритмы работали. Вот где в игру вступает концепция CommonReference. Эта концепция в основном требует, чтобы два типа совместно использовали «общий ссылочный тип», в который оба могут быть преобразованы. Он по существу делегирует черты типа common_reference, поведение которых подробно описано в cppreference (однако, обратите внимание, что то, что описано, относится к вещам в диапазонах TS, что может быть не совсем точно такой же, как у библиотеки range-v3).

На практике концепцию Readable можно удовлетворить, определив оператор преобразования в типе, возвращаемом оператором *. Вот простой пример, который проходит тест (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>>{} );
...