#c #c 20 #std-ranges
#c #c 20 #стандартные диапазоны
Вопрос:
Я довольно новичок в диапазонах, и я хотел бы знать, есть ли способ применить динамическое количество адаптеров диапазона. Я некоторое время возился с некоторым кодом, а также провел некоторые поиски, но безрезультатно.
#include <iostream>
#include <ranges>
int main() {
auto output = std::ranges::views::iota(2, 100);
for (int i = 2; i < 100; i ) {
output = output | std::ranges::views::filter([i](int num){ return num % i != 0 || num == i; });
}
std::cout << "The 10th prime is: " << output[9] << "n";
}
По сути, я хочу что-то подобное, но это выдает ошибку компиляции ( no match for 'operator='
). Кажется, что для каждого применения адаптера диапазона требуется новый тип, поэтому мы не можем динамически создавать этот диапазон. Есть ли какой-то способ обойти это?
Комментарии:
1. На самом деле было бы гораздо разумнее просто … фильтровать фактический контейнер. Зачем использовать отложенный просмотр, когда вы можете просто применить каждый фильтр по мере необходимости?
Ответ №1:
Для такого фиксированного числа можно было бы использовать метапрограммирование для рекурсивного построения диапазона (хотя вы можете столкнуться с ограничением глубины создания экземпляра шаблона). Вы можете создать действительно динамическое число, стирая диапазоны, чтобы цепочка фильтров была связана вызовами виртуальных функций. Результат медленный, а код болезненный, но это, безусловно, возможно.
Ответ №2:
Одной из альтернатив является сохранение результатов каждой фильтрации в a vector
, что гарантирует, что тип диапазона после каждой операции является согласованным и может быть переназначен.
#include <iostream>
#include <ranges>
#include <vector>
auto to_vector(std::ranges::view auto view) {
return std::vector(view.begin(), view.end());
}
int main() {
auto output = to_vector(std::views::iota(2, 100));
for (int i = 2; i < 100; i ) {
output = to_vector(output | std::views::filter(
[i](int num){ return num % i != 0 || num == i; }));
}
std::cout << "The 10th prime is: " << output[9] << "n";
}
Однако это неэффективно и не является хорошим вариантом использования адаптеров диапазона. Поэтому вам, возможно, придется использовать более эффективные алгоритмы для реализации этого.
Ответ №3:
В этом конкретном случае вы можете вместо этого создать предикат фильтра:
int main() {
auto output = std::views::iota(2, 100);
std::function<bool(int)> filter_fn = [] (int) { return true; };
for (int i = 2; i < 100; i )
{
filter_fn = [=] (int num) {
return filter_fn(num) amp;amp; (num % i != 0 || num == i);
};
}
auto primes = output | std::views::filter(filter_fn);
std::cout << "The 10th prime is: " <<
(primes | std::views::drop(9)).front() << "n";
}
Однако «Может» не означает «должен». Это довольно неэффективно, поскольку создает цепочку косвенных вызовов для предиката.