c++ - 在 C++20 中,我如何编写一个连续的迭代器?
问题描述
C++20 对std::contiguous_iterator_tag
. 一些 STL 算法(例如std::copy
)可以在连续迭代器上执行得更好。但是,我不清楚程序员应该如何访问这个新功能。
让我们假设我们有一个完全符合 C++20 库的实现。我想编写最简单的连续迭代器。
#include <iterator>
class MyIterator {
int *p_;
public:
using value_type = int;
using reference = int&;
using pointer = int*;
using difference_type = int;
using iterator_category = std::contiguous_iterator_tag;
int *operator->() const;
int& operator*() const;
int& operator[](int) const;
MyIterator& operator++();
MyIterator operator++(int);
MyIterator& operator--();
MyIterator operator--(int);
MyIterator& operator+=(int);
MyIterator& operator-=(int);
friend auto operator<=>(MyIterator, MyIterator) = default;
friend int operator-(MyIterator, MyIterator);
friend MyIterator operator+(MyIterator, int);
friend MyIterator operator-(MyIterator, int);
friend MyIterator operator+(int, MyIterator);
};
namespace std {
int *to_address(MyIterator it) {
return it.operator->();
}
}
static_assert(std::contiguous_iterator<MyIterator>); // FAILS
这在 GCC/libstdc++ 和 MSVC/STL 上都失败了;但它应该吗?
对于我的下一次尝试,我专门研究了pointer_traits<MyIterator>
. 实际上不是指针,所以除了库需要的一个函数之外MyIterator
,我没有放入任何东西。这是我的第二次尝试:pointer_traits
#include <iterator>
class MyIterator {
~~~
};
template<>
struct std::pointer_traits<MyIterator> {
int *to_address(MyIterator it) {
return it.operator->();
}
};
static_assert(std::contiguous_iterator<MyIterator>); // OK!
这是我应该做的吗?感觉非常hacky。(要明确:我的第一次失败尝试也感觉非常糟糕。)
我错过了一些更简单的方法吗?
特别是,有什么方法MyIterator
可以保证它是连续的,只使用成员和朋友以及可以在类的主体中定义的东西吗?就像, ifMyIterator
是在一些深度嵌套的命名空间中定义的,我不想为了打开namespace std
.
编辑添加:Glen Fernandes 告诉我有一个更简单的方法——我应该像这样添加一个element_type
typedef !(而且我可以iterator_traits
在 C++20 中删除 3 个大 5 类型定义。)这看起来更好!
#include <iterator>
class MyIterator {
int *p_;
public:
using value_type = int;
using element_type = int;
using iterator_category = std::contiguous_iterator_tag;
int *operator->() const;
int& operator*() const;
int& operator[](int) const;
MyIterator& operator++();
MyIterator operator++(int);
MyIterator& operator--();
MyIterator operator--(int);
MyIterator& operator+=(int);
MyIterator& operator-=(int);
friend auto operator<=>(MyIterator, MyIterator) = default;
friend int operator-(MyIterator, MyIterator);
friend MyIterator operator+(MyIterator, int);
friend MyIterator operator-(MyIterator, int);
friend MyIterator operator+(int, MyIterator);
};
static_assert(std::contiguous_iterator<MyIterator>); // OK!
此外,我还看到了一些关于使用成员 typedefiterator_concept
而不是iterator_category
. 为什么我想提供MyIterator::iterator_concept
?(我不是在这里要求完整的历史解释;只是一个简单的最佳实践指南“不,忘记iterator_concept
”或“是的,因为它对 X 有帮助”会很好。)
解决方案
C++ 概念作为语言特性的主要好处之一是概念定义告诉您需要提供什么。std::contiguous_iterator
也不例外。是的,您可能需要深入挖掘 10 层以上的其他概念定义,才能了解您需要提供的基本术语。但它们就在语言中。
惠特,连续迭代器是随机访问迭代器:
- 来自谁的标签
contiguous_iterator
- 谁
reference_type
是对其的左值引用value_type
(即:不是代理迭代器或生成器)。 - 为此,可以调用标准库函数
std::to_address
将任何有效的迭代器(即使是不可取消引用的迭代器)转换为指向迭代器引用的元素或指向过去的指针的指针。
不幸的是,不能通过std::to_address
直接专业化来满足#3。您必须为您的迭代器类型使用specialize pointer_traits::to_address
,或者给您的迭代器一个operator->
重载。
后者更容易做到,尽管operator->
没有其他要求,但std::contiguous_iterator
对于这样的迭代器类型是有意义的:
class MyIterator
{
...
int const *operator->() const;
};
仅供参考:如果您基于概念约束模板,而不是仅仅在其上进行约束,大多数编译器都会为您提供更多有用的错误消息static_assert
。像这样:
template<std::contiguous_iterator T>
void test(const T &t);
test(MyIterator{});
推荐阅读
- java - 将 12 字符转换为长
- f# - 如何在 F# 中找到数组的第一个 NaN?
- python - 如何在codeigniter中调用python?
- android - 如何从 Android(kotlin)中 Firebase 提供的照片 uri 中检索和存储用户个人资料图片?
- python - 如何使用 CDK 部署 aws EventBridge - Python 示例
- javascript - 成功安装电子js有问题
- asynchronous - 如何知道 Mulesoft 异步范围何时完成
- android - 如何在 Android 上调试 Opera?
- python - datetime.time 对象在尝试使用它时返回错误?
- powerbi - Power BI 自定义列:不同表中的匹配日期和客户 ID