Как скрыть сложный тип диапазона от диапазона-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