是否可以使用动态数量的范围适配器?

Is it possible to use a dynamic number of range adaptors?

我对范围相当陌生,我想知道是否有办法应用动态数量的范围适配器。摸索了一段时间的代码,也搜索了一下,都没有结果。

#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=')。好像每申请一个range adapter都需要一个新的类型,所以我们没法动态创建这个range。有解决办法吗?

对于像这样的固定数字,可以使用元编程递归地构建范围(尽管您可能会达到模板实例化深度限制)。您可以通过 type-erasing 范围来创建真正的动态数字,这样过滤器链就可以通过虚函数调用连接起来。结果很慢,代码很痛苦,但肯定是可以的。

其中一种方案是将每次过滤的结果存储在一个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";
}

Demo.

但是,这是低效的,不是使用范围适配器的好用例。所以你可能需要使用更高效的算法来实现这个。

在这种特殊情况下,您可以改为构建过滤谓词:


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) && (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";
}

可以并不意味着应该。这是非常低效的,因为它为谓词创建了一系列间接调用。