lis*_*rus 4 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));
}
Run Code Online (Sandbox Code Playgroud)
得知(至少在 GCC-10.3.0 和 GCC-12.0.0 下)这段代码卡在std::prev.
什么情况是,由于拉姆达不返回左值引用,transformed范围迭代器被列为输入迭代器(参见规则进行iterator_category选择的views::transform)。但是,std::prev 要求迭代器至少是双向迭代器,所以我猜这段代码实际上是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;
}
Run Code Online (Sandbox Code Playgroud)
被调用__n == -1,这解释了观察到的行为。
如果我们std::prev用手动迭代器递减替换,一切正常。切换到std::ranges::prev 工作,太。
现在,我不能std::prev只对std::vector. 虽然存在一个简单的解决方案,但我非常担心标准库的新旧范围操作部分之间这种意想不到的相互作用。所以,我的问题是:这是一个已知问题std::ranges吗,在使用新范围时我真的应该忘记命名空间中没有的所有内容,并重写所有现有代码以确保它们适用于新范围吗?
根据 C++17 的计算,它不是随机访问迭代器。transform必须返回一个值而不是 a reference,并且 C++17 的迭代器类别不允许在 InputIterator 之上的任何东西。
但是这种类型是std::random_access_iteratorC++20 的规则,它允许在连续以下的任何迭代器/范围上使用类似代理的迭代器。
std::prev是一个 pre-C++20 工具,所以它按照 pre-C++20 规则工作。如果您需要使用 C++20 规则,则必须使用C++20 等效项:std::ranges::prev .
现在,我不能对 std::vector 的视图执行 std::prev 显然是荒谬的。
不,有必要。C++20 的概念化迭代器类别比以前的 C++ 版本中的迭代器类别限制更少。这意味着有些迭代器不能在 C++20 之前的代码中使用,而可以在 C++20 基于范围的代码中使用。
这就是为什么我们在ranges命名空间中有这些东西的新函数。
| 归档时间: |
|
| 查看次数: |
118 次 |
| 最近记录: |