如何将智能迭代器传递给接受经典迭代器的函数?

6
我正在尝试熟悉将成为C++20标准一部分的ranges-v3库。为此,我正在尝试通过使用新的可用构造替换(适当的情况下)经典迭代器和算法来重构一些玩具代码。在这个特定的例子中,我无法想象如何将调用ranges::min_element(替换了对std::min_element的调用)返回的迭代器传递给另一个接受经典iterator作为参数的函数。
我已经在文档内搜索了一些类似于smartIt2classicIt的函数,但没有成功。
以下是一个最小化的示例:
void f(std::vector<int>& v, std::vector<int>::iterator it); // old function that I want to reuse
auto predicate = [](int i){ return true; }; // check function

std::vector<int> v;
// auto min_el = std::min_element(...); // old code
auto filtered_range = v | ranges::view::filter(predicate); // to avoid a dangling iterator
auto min_el = ranges::min_element(filtered_range);

f(v, min_el); // pass min_el to f: doesn't compile with the new code

起初,我以为ranges::min_element的结果可以隐式转换为经典迭代器,但我错了:编译器返回了一个长长的错误,说无法将ranges::basic_iterator bla bla bla转换为std::vector bla bla bla迭代器。根据这个错误,我推断出ranges::min_element确实返回某种类型的迭代器,但如何以旧有的方式使用它呢?
我看到三种可能的解决方案:
  1. 改变将min_el传递给f的方式
  2. 改变f的第二个参数的类型(可能是向后兼容的方式)
  3. 同时改变两者
但我无法想出如何实现它们。也许还有其他解决方案?我还看到另一个可能的问题来源,因为返回的迭代器可能指的是filtered_range而不是v... 欢迎任何帮助!

它不会成为C++20标准的一部分。并非全部都是如此。您应该关注C++20实际上有什么 - Nicol Bolas
2
@NicolBolas 您的措辞可能会被理解为非常不友善... - Max Langhof
2
想象一下你做了这样的事情:min_el = v | takeEverySecondElement()。增加 min_el 当然应该使向量内部前进两个元素,但是 f 可能希望传递的迭代器仅前进一个元素。也许你真正想要的是向量中的索引?基本上,尝试在头脑中更改问题的各个部分,并查看您期望的结果以及它暴露的要求/冲突(例如,如果您有一个 std::map 而不是一个向量会怎样?) - Max Langhof
1
请注意,f 复制了 v(您通过值传递它)。这是有意为之吗?这意味着您传递给 f 的迭代器指向的是与 f 内部拥有的不同的向量。 - sebrockm
@NicolBolas 对不起,出现错误了,请随意编辑我的问题以修复它! - Rackbox
显示剩余2条评论
1个回答

3
你可以通过base()成员函数将一个适配的迭代器转换为其基础迭代器。
例如:
std::vector<int>::const_iterator foo(std::vector<int> const& v) {
    auto filtered = v | ranges::view::filter([](int i){return i > 5;});
    auto min_el = ranges::min_element(filtered);
    return min_el.base();
}

请注意,这只会移除一层包装,而不是直接到最底层。所以如果你有另一个适配器,则需要另一个base()
std::vector<int>::const_iterator foo(std::vector<int> const& v) {
    auto filtered = v | ranges::view::filter([](int i){return i > 5;})
                      | ranges::view::transform([](int i){ return i * i; });
    auto min_el = ranges::min_element(filtered);
    return min_el.base().base();
}

太棒了!有没有办法让它在一行内完成?我试图直接输入 return ranges::min_element(v | ...).base();, 但是现在 ranges::min_element 的返回值是 ranges::dangling 类型,而且没有 base() 成员。有可能的解决方法吗? - Rackbox
1
@Rackbox 这是有意为之的,旨在使悬挂迭代器的潜在风险更加明显(虽然这种情况是安全的,但语言无法很好地区分安全和不安全的使用方式)。 - Barry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接