Как скрыть сложный тип диапазона от диапазона-v3?

Мне нужен класс с методом, который возвращает некоторый диапазон, используя библиотеку range-v3. Чтобы реализовать такой класс, я могу написать все правильно в определении этого класса. Например:

#include <iostream>
#include <set>
#include <range/v3/view/transform.hpp>

class Alpha {
public:
  int x;
};

class Beta : public Alpha {};

class Foo {
public:
  std::set<Alpha*> s;

  auto r() { return s | ranges::v3::view::transform([](Alpha* a) { return static_cast<Beta*>(a); }) }
};

Однако в моем реальном случае Foo::r Функция довольно сложна, и я хотел бы скрыть ее реализацию. В частности, реализация использует некоторые дополнительные библиотеки, которые в противном случае не нужно включать, когда класс Foo объявлен

Тем не менее, когда определение Foo::r отделен от своего объявления, его тип возвращаемого значения должен быть указан явно. decltype приходит с некоторой помощью:

Заголовочные файлы:

class Foo {
public:
  std::set<Alpha*> s;

  using RangeReturn = decltype(std::declval<std::set<Alpha*>&>() | ranges::v3::view::transform(std::function<Beta*(Alpha*)>()));
  RangeReturn r();
};

Реализация, файл cpp:

#include "Foo.h"

Foo::RangeReturn Foo::r() {
    return s | ranges::v3::view::transform(std::function<Beta*(Alpha*)>{
      [](Alpha* a) { return static_cast<Beta*>(a); }
      });
}

Это делает непосредственную работу по сокрытию фактической реализации Foo::r, Однако тип возвращаемого значения по-прежнему эффективно "пропускает" информацию о том, как строится диапазон. Что, вероятно, хуже, теперь я вынужден явно использовать std::function объект в диапазоне трубопровода.

Но действительно ли эта информация нужна пользователю возвращаемого диапазона? Все пользователи Foo::r волнует то, что это какая-то итерация. Она имеет:

  • begin() давая итератор к началу диапазона
  • end() давая некоторый итератор или страж
  • Итератор может быть увеличен, чтобы перебрать диапазон
  • Итератор может быть разыменован, давая некоторый тип T (Beta* в примере кейса).

Пользователю все равно, есть ли представление преобразования или нет, а также количество преобразований, фильтров и тому подобное.

Итак, мой вопрос - есть ли способ скрыть всю эту информацию? Я хотел бы иметь возможность написать что-то вроде этого:

В шапке:

class Foo {
public:
  std::set<Alpha*> s;

  Iterable<Beta*> r();
};

В файле cpp:

#include "Foo.h"

Iterable<Beta*> Foo::r() {
    return s | ranges::v3::view::transform([](Alpha* a) { return static_cast<Beta*>(a); });
}

Я могу принять тот факт, что производится Iterable Тип может содержать реальные вызовы функций, которые не могут быть встроены из-за процесса сокрытия. Оптимизация во время соединения может или не может быть в состоянии оптимизировать это позже.

К сожалению, насколько мне известно, такие Iterable это просто концепция, а не отдельный тип в библиотеке.

1 ответ

Решение

Тип возврата r() вы ищете это ranges::v3::any_view<Beta*>,

Обратите внимание, что применяется стирание типов, которое подразумевает некоторое снижение производительности во время выполнения, которое может быть значительным. Актуальное обсуждение: низкая производительность стертых представлений.

Демонстрационная версия: https://wandbox.org/permlink/JylKIHD0NaQsRXdB

Другие вопросы по тегам