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