C++ 复杂范围的多个迭代器

C++ 复杂范围的多个迭代器,c++,range-v3,C++,Range V3,我正在尝试将多个迭代器扩展到更复杂的范围(使用range-v3库)——手动实现笛卡尔乘积,使用过滤器,对每个和产生。然而,当我试图将多个迭代器保持在这样的范围内时,它们共享一个公共值。例如: #include <vector> #include <iostream> #include <range/v3/view/for_each.hpp> #include <range/v3/view/filter.hpp> int main() {

我正在尝试将多个迭代器扩展到更复杂的范围(使用range-v3库)——手动实现笛卡尔乘积,使用
过滤器
对每个
产生
。然而,当我试图将多个迭代器保持在这样的范围内时,它们共享一个公共值。例如:

#include <vector>
#include <iostream>
#include <range/v3/view/for_each.hpp>
#include <range/v3/view/filter.hpp>

int main() {
    std::vector<int> data1{1,5,2,7,6};
    std::vector<int> data2{1,5,2,7,6};
    auto range =
            data1
            | ranges::v3::view::filter([](int v) { return v%2; })
            | ranges::v3::view::for_each([&data2](int v) {
                return data2 | ranges::v3::view::for_each([v](int v2) {
                    return ranges::v3::yield(std::make_pair(v,v2));
                });
            });
    auto it1 = range.begin();
    for (auto it2 = range.begin(); it2 != range.end(); ++it2) {
        std::cout << "[" << it1->first << "," << it1->second << "] [" << it2->first << "," << it2->second << "]\n";
    }
    return 0;
}
  • 为什么呢
  • 我怎样才能避免这种情况
  • 如何保持多个独立的迭代器指向范围的不同位置


而不是在上面的MCVE中反映的,考虑一个用例,有人试图实现类似于代码> STD::Max元素——试图将迭代器返回到交叉产品中的最高值对。在寻找最高值时,需要将迭代器存储到当前最佳候选值。它不能在搜索时更改,如果您需要范围的副本(如其中一个答案中所建议的),则管理迭代器会很麻烦


实现整个交叉积也不是一个选项,因为它需要大量内存。毕竟,将范围与过滤器和其他动态转换一起使用的目的是避免这种具体化。

迭代器是指向向量中某个元素的指针,在这种情况下,它1指向向量的开头。因此,如果您试图将迭代器指向向量的相同位置,它们将是相同的。但是,可以有多个迭代器指向向量的不同位置。希望这能回答您的问题。

结果视图似乎存储了状态,结果是单次通过。您只需制作所需数量的视图副本即可解决此问题:

int main() {
    std::vector<int> data1{1,5,2,7,6};
    std::vector<int> data2{1,5,2,7,6};
    auto range =
            data1
            | ranges::v3::view::filter([](int v) { return v%2; })
            | ranges::v3::view::for_each([&data2](int v) {
                return data2 | ranges::v3::view::for_each([v](int v2) {
                    return ranges::v3::yield(std::make_pair(v,v2));
                });
            });

    auto range1= range;         // Copy the view adaptor
    auto it1 = range1.begin();

    for (auto it2 = range.begin(); it2 != range.end(); ++it2) {
        std::cout << "[" << it1->first << "," << it1->second << "] [" << it2->first << "," << it2->second << "]\n";
    }

    std::cout << '\n';
    for (; it1 != range1.end(); ++it1) { // Consume the copied view
        std::cout << "[" << it1->first << "," << it1->second << "]\n";
    }
    return 0;
}

这是怎么回事?

这里的整个问题源于这样一个事实,即要求其参数为,而由
ranges::v3::yield
创建的范围显然(显然?)只提供。不幸的是,本文没有明确提到可以预期的迭代器类别(至少我没有发现有人提到它)。这确实是一个巨大的增强,因为所有标准库算法都明确说明了它们需要什么迭代器类别


std::max_element
的特殊情况下,您不是第一个遇到
ForwardIterator
而不仅仅是
inputiiterator
这一违反直觉的要求的人,请参见示例。总之,这是有意义的,因为
std::max_element
不返回max元素,而是返回max元素的迭代器。因此,为了使
std::max_元素
能够使用它,特别是
inputierator
上缺少的


由于这个原因,许多其他标准库函数也不能与
std::max_element
一起使用,例如,这确实是一个遗憾:您无法从具有现有标准库的文件中获取max element!您要么必须首先将整个文件加载到内存中,要么必须使用自己的max算法

标准库只是缺少一个真正返回max元素的算法,而不是一个指向max元素的迭代器。这种算法也可以与
inputierator
s一起使用。当然,这可以很容易地手动实现,但是由标准库给出它还是很方便的。我只能推测它为什么不存在。可能一个原因是,它需要
value\u type
是可复制构造的,因为
inputierator
不需要返回对元素的引用,而max算法生成副本可能违反直觉


那么,现在关于你的实际问题:

这是为什么?(即,为什么您的范围只返回
inputierator
s?)

显然,
yield
会动态创建值。这是出于设计,这正是人们希望使用收益率的原因:不必预先创建(并存储)范围。因此,我不知道如何实现收益率,使其满足以下要求,特别是第二个问题让我头疼:

  • 如果a和b比较相等(a==b在上下文中可转换为true),则它们要么都是不可解引用的,要么*a和*b是绑定到同一对象的引用

从技术上讲,我可以想象一个人可以实现
yield
,使从一个范围创建的所有迭代器共享一个公共的内部存储,该存储在第一次遍历期间动态填充。然后,不同的迭代器可以为您提供对底层对象的相同引用。但是,
std::max_element
会默默地消耗
O(n²)
内存(笛卡尔乘积的所有元素)。因此,在我看来,最好不要这样做,而是让用户自己具体化范围,这样他们就会意识到这一点

如何避免这种情况?


正如metalfox所说,您可以复制视图,这将导致不同的范围,从而产生独立的迭代器。尽管如此,这仍然不能使
std::max_元素
起作用。因此,考虑到
yield
的性质,这个问题的答案是:你无法通过
yield
或任何其他动态创造价值的技术来避免这一点

如何保持多个独立的迭代器指向范围的不同位置?

这与前面的问题有关。基本上,这个问题会自己回答:如果您想在不同的位置指向独立的迭代器,这些位置必须存在于内存中的某个地方。因此,您需要具体化至少那些曾经有一个迭代器指向它们的元素,在int main() { std::vector<int> data1{1,5,2,7,6}; std::vector<int> data2{1,5,2,7,6}; auto range = data1 | ranges::v3::view::filter([](int v) { return v%2; }) | ranges::v3::view::for_each([&data2](int v) { return data2 | ranges::v3::view::for_each([v](int v2) { return ranges::v3::yield(std::make_pair(v,v2)); }); }); auto range1= range; // Copy the view adaptor auto it1 = range1.begin(); for (auto it2 = range.begin(); it2 != range.end(); ++it2) { std::cout << "[" << it1->first << "," << it1->second << "] [" << it2->first << "," << it2->second << "]\n"; } std::cout << '\n'; for (; it1 != range1.end(); ++it1) { // Consume the copied view std::cout << "[" << it1->first << "," << it1->second << "]\n"; } return 0; }
template <typename InputRange,typename BinaryPred = std::greater<>>
auto my_max_element(InputRange &range1,BinaryPred &&pred = {}) -> decltype(range1.begin()) {
    auto range2 = range1;
    auto it1 = range1.begin();
    std::ptrdiff_t pos = 0L;

    for (auto it2 = range2.begin(); it2 != range2.end(); ++it2) {
        if (pred(*it2,*it1)) {
            ranges::advance(it1,pos);   // Computing again the sequence as the iterator advances!
            pos = 0L;
            }
        ++pos;
        }
    return it1; 
}