c++ - range::view::transform 产生一个 InputIterator 阻止使用 std::prev

标签 c++ iterator c++20 std-ranges

考虑以下代码,它使用 C++20 中的 Ranges 库:

#include <vector>
#include <ranges>
#include <iostream>

int main()
{
    std::vector<int> v{0,1,2,3,4,5,6,7};

    auto transformed = std::ranges::views::transform(v, [](int i){ return i * i; });

    std::cout << *std::prev(std::end(transformed));
}
我很惊讶地得知(至少在 GCC-10.3.0 和 GCC-12.0.0 下)这段代码 gets stuck in std::prev .
发生的情况是,由于 lambda 不返回左值引用,transformed范围迭代器被归类为输入迭代器(请参阅 rulesiterator_category 选择 views::transform )。然而,std::prev requires迭代器至少是一个双向迭代器,所以我猜这段代码实际上是 UB。在 libstdc++ 中应用 std::prev到输入迭代器导致此函数
template<typename _InputIterator, typename _Distance>
__advance(_InputIterator& __i, _Distance __n, input_iterator_tag)
{
    // concept requirements
    __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
    __glibcxx_assert(__n >= 0);
    while (__n--)
        ++__i;
}
被调用 __n == -1 ,这解释了观察到的行为。
如果我们替换 std::prev手动迭代器递减,everything works fine .切换到 std::ranges::prev works, too .
现在,我不能这样做显然是荒谬的std::prev关于什么只是俯瞰 std::vector .虽然存在一个简单的解决方案,但我非常担心标准库的新旧范围操作部分之间这种意想不到的相互作用。所以,我的问题是:这是一个已知问题吗,我真的应该忘记std::ranges 中没有的所有内容吗?使用新范围时的命名空间,并重写所有现有代码以确保它们适用于新范围 ?

最佳答案

根据 C++17 的计算,它不是随机访问迭代器。 transform必须返回一个值而不是 reference和 C++17 的迭代器类别不允许在 InputIterator 之上的任何东西。
但这种类型是std::random_access_iterator通过 C++20 的规则,它允许在连续以下的任何迭代器/范围上使用类似代理的迭代器。std::prev是一个 pre-C++20 工具,所以它按照 pre-C++20 规则工作。如果您需要使用 C++20 规则,则必须使用 the C++20 equivalent: std::ranges::prev .

Now, it is clearly nonsensical that I can't do std::prev on what is just a view over an std::vector.


不,这是必要的。 C++20 的概念化迭代器类别比以前的 C++ 版本中的迭代器类别限制更少。这意味着有些迭代器不能在 C++20 之前的代码中使用,而可以在 C++20 基于范围的代码中使用。
这就是为什么我们在 ranges 中为这些东西添加了新功能的原因。命名空间。

关于c++ - range::view::transform 产生一个 InputIterator 阻止使用 std::prev,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68100775/

相关文章:

c++ - 如何将可变参数传递给exe?

c++ - 双线性插值以放大位图图像

c++ - 围绕 OpenGL 场景中的对象旋转有困难

c++ - 结构的内存分配(低性能)

Scala - 迭代器和 takeWhile

c++ - DLL 卸载后使用 std::weak_ptr

c++ - C++20 中是否有 boost::irange 等价物?

c++ - 高效迭代大量数据

java - Arraylist.contains 不会检查字符串

c++ - 范围的 std::lexicographical_compare_three_way