如何编写适配ranges::find_if的带哨位自定义可迭代类?
自定义异步可迭代容器与迭代器的标准实现
核心概念要求
要适配std::ranges::find_if这类C++20算法,自定义类型必须满足以下标准概念约束:
- 迭代器需符合
std::input_iterator概念 - 哨位类型需满足
std::sentinel_for<迭代器类型>概念 - 容器需符合
std::ranges::input_range概念;若要支持直接传容器的范围版算法,还需满足std::ranges::borrowed_range(确保迭代器不会提前悬垂)
正确实现示例
假设你的NetworkInfo基础结构如下:
struct NetworkInfo { bool IsConnected() const { // 自定义连接状态判断逻辑 return true; } // 其他成员变量/方法 };
1. 自定义输入迭代器IteratorAsync
#include <iterator> #include <concepts> class IteratorAsync { public: // 迭代器标准类型别名 using value_type = NetworkInfo; using reference = const NetworkInfo&; using pointer = const NetworkInfo*; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; // 默认构造、拷贝、赋值、析构(编译器默认生成即可) IteratorAsync() = default; IteratorAsync(const IteratorAsync&) = default; IteratorAsync& operator=(const IteratorAsync&) = default; ~IteratorAsync() = default; // 前置递增(输入迭代器必须实现) IteratorAsync& operator++() { // 迭代器前进逻辑:比如移动到下一个NetworkInfo条目 ++current_idx; return *this; } // 后置递增(可选,但建议实现以符合标准) IteratorAsync operator++(int) { auto temp = *this; ++*this; return temp; } // 解引用(必须) reference operator*() const { // 返回当前指向的NetworkInfo常量引用 return get_current_item(); } // 箭头运算符(可选,方便访问成员) pointer operator->() const { return &operator*(); } // 与哨位的相等比较(用于满足sentinel_for概念) bool operator==(const class IteratorAsyncSentinel& sentinel) const; private: size_t current_idx = 0; NetworkInfo get_current_item() const { // 实际获取对应索引的NetworkInfo逻辑 return NetworkInfo{}; } };
2. 自定义哨位IteratorAsyncSentinel
class IteratorAsyncSentinel { public: IteratorAsyncSentinel() = default; // 与迭代器的相等比较(双向实现,满足sentinel_for对称性要求) bool operator==(const IteratorAsync& iter) const { // 判断迭代器是否到达末尾的逻辑:比如索引超出总条目数 return iter.current_idx >= get_total_count(); } // 反向比较(友元函数实现对称性) friend bool operator==(const IteratorAsync& iter, const IteratorAsyncSentinel& sentinel) { return sentinel == iter; } private: size_t get_total_count() const { // 返回网络列表的总条目数 return 5; } }; // 补全迭代器类中的==运算符实现 bool IteratorAsync::operator==(const IteratorAsyncSentinel& sentinel) const { return sentinel == *this; }
3. 自定义可迭代容器IterableAsync
#include <ranges> class IterableAsync { public: IteratorAsync begin() const { return IteratorAsync{}; } IteratorAsyncSentinel end() const { return IteratorAsyncSentinel{}; } }; // 特化enable_borrowed_range,告知标准库该容器的迭代器不会在容器存活期间悬垂 template<> inline constexpr bool std::ranges::enable_borrowed_range<IterableAsync> = true;
验证使用
现在两种形式的std::ranges::find_if都可以正常编译:
#include <algorithm> int main() { IterableAsync network_list; // 范围版本调用 auto it1 = std::ranges::find_if(network_list, [](const NetworkInfo& item) { return item.IsConnected(); }); // 迭代器对版本调用 auto it2 = std::ranges::find_if(network_list.begin(), network_list.end(), [](const NetworkInfo& item) { return item.IsConnected(); }); return 0; }
关键修复点说明
- 迭代器合规性:确保
IteratorAsync实现了输入迭代器的所有必要操作(递增、解引用、相等比较) - 哨位兼容性:
IteratorAsyncSentinel与IteratorAsync必须实现双向相等比较,满足std::sentinel_for概念 - borrowed_range支持:通过特化
std::ranges::enable_borrowed_range,让标准库允许直接使用容器调用范围版算法 - 类型别名正确性:迭代器的
value_type、reference等别名必须严格符合标准定义,否则概念检查会失败
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

