Концепция C ++, которая требует функции-члена с OutputIterator в качестве параметра - PullRequest
2 голосов
/ 26 апреля 2020

Я играю с концепциями и нахожу контрольно-пропускной пункт. Или, может быть, заблокирован только мой разум.

Я хочу создать класс, который буферизует «объемно читаемый» источник данных. Такой источник данных должен иметь функцию-член, которая принимает OutputIterator и имеет такую ​​подпись:

template<typename It>
size_t read(It firstItem, size_t max)

Моя идея заключалась в том, чтобы определить концепцию BulkReadable, аналогичную:

template<typename Source>
concept bool BulkReadable = 
    requires(Source s, Iter out, size_t max) {
        {s.read(out, max)} -> size_t;
    };

I У меня проблемы с указанием Iter. Я мог бы добавить другое имя типа в список параметров шаблона, но тогда классу Buffer, который хочет использовать концепцию, нужно будет указать тип этого параметра.

Идеальный способ, которым я хотел бы использовать концепцию, это :

template<BulkReadable Source>
class Buffer {
  public: 
    Source& input:
    Buffer(Source& input) : input(input){}
    ...     

Является ли этот подход жизнеспособным? Если да, как я могу требовать шаблонную подпись метода, если я не хочу / могу указать тип?

1 Ответ

3 голосов
/ 26 апреля 2020

Это распространенная проблема задавать неправильный вопрос о концепции, когда вы пытаетесь использовать их так же, как интерфейс базового класса. С базовыми классами вы объявляете точные, определенные c функции, которые должны реализовывать производные классы. Вы хотите, чтобы пользователь реализовал именно ту функцию, которую, по его словам, он должен.

С концепциями вы подходите к вопросу с другой стороны: какое использование вы пытаетесь создать?

В какой-то момент в вашем коде у вас есть некоторый объект, некоторый итератор и размер. И вы собираетесь взять этот объект, вызвать функцию, передав ей итератор и размер, и вы ожидаете ответ определенного типа. И этот процесс имеет некоторое значение.

Тогда , что - это ваша концепция. Это концепция, основанная как минимум на 2 параметрах: тип объекта и тип итератора. Это то, что вы должны создать.

Если у вас есть это ограничение BulkReadable, то у вас должен быть какой-то интерфейс, ограниченный этим. Интерфейс, который будет называться read. Чтобы вызвать read, этот интерфейс должен иметь итератор.

Итак, вот варианты:

  1. Интерфейсу дается тип итератора (прямо или косвенно) Пользователь. Если это так, то вы просто используете этот тип в ограничении BulkReadable функции. Если тип итератора основан на сложном наборе операций над параметрами, вам придется выполнить некоторые вычисления для вычисления типа итератора.

  2. Итератор определяется статически. Затем просто используйте известный тип итератора в ограничении.

Дело в том, что в точке, где вы собираетесь попытаться вызвать read, вы знаете что такое тип итератора. И, следовательно, вы можете ограничить вещи, используя этот тип. Следовательно, ваша концепция на самом деле не BulkReadable, а BulkReadableFrom.

Короче говоря, вы не должны ограничивать тип возможностью принимать любой тип (или любой тип в рамках некоторых ограничений). Сравните ограничения с фактическими типами, с которыми вы собираетесь их использовать, предпочтительно в том месте, где они становятся релевантными.

...