如何编写适配C++20 std::ranges的自定义前向迭代器及相关疑问
std::ranges 库的C类? 问题背景
编写带迭代器的外观类时,clang++18编译器提示未满足std::ranges要求,深层错误为:
/usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/ranges:946:30: note: because '__adaptor::__is_range_adaptor_closure_fn(__t, __t)' would be invalid: no matching function for call to '__is_range_adaptor_closure_fn'
疑问解答
1. 为什么Iterator需要支持默认构造?
C++20的std::forward_iterator概念强制要求迭代器可默认构造,这是因为很多range适配器(比如std::views::filter)的内部逻辑依赖于创建临时迭代器实例。你的原实现使用了大对象的const引用,直接默认构造会导致悬空引用,解决方式是将引用替换为指针,这样可以默认构造(初始化为nullptr),后续使用时保证指针指向有效对象即可。
2. 实现std::forward_iterator为何需要difference_type?
std::forward_iterator继承自std::input_iterator概念,而input_iterator要求必须定义difference_type这一关联类型——哪怕你不需要实现operator--或operator-这类双向/随机访问迭代器的操作。对于forward迭代器,直接将difference_type定义为std::ptrdiff_t即可,无需额外实现差值计算逻辑。
修正后的代码
#include <cstddef> #include <iterator> #include <unordered_set> #include <concepts> using Set = std::unordered_set<std::ptrdiff_t>; class MyFacadeClass { private: class Iterator { public: using iterator_category = std::forward_iterator_tag; using value_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t; using pointer = const std::ptrdiff_t*; using reference = const std::ptrdiff_t&; // 默认构造函数,满足forward_iterator要求 Iterator() : pSelf(0), pA(nullptr), pB(nullptr), pIt(), pInB(false) {} Iterator(const std::ptrdiff_t self, const Set* a, const Set* b, const Set::const_iterator it, const bool inB = false) : pSelf(self), pA(a), pB(b), pIt(it), pInB(inB) {} reference operator*() const { return *pIt; } pointer operator->() const { return &*pIt; } // 补充==运算符,满足equality_comparable要求 bool operator==(const Iterator& other) const { return pIt == other.pIt; } bool operator!=(const Iterator& other) const { return !(*this == other); } // 前置递增 Iterator& operator++() { if (pInB) { ++pIt; return *this; } else { do { ++pIt; if (pIt == pA->end()) { pIt = pB->begin(); pInB = true; return *this; } } while (*pIt == pSelf); return *this; } } // 后置递增,满足forward_iterator要求 Iterator operator++(int) { Iterator temp = *this; ++*this; return temp; } private: const std::ptrdiff_t pSelf; const Set* pA; // 替换引用为指针,支持默认构造 const Set* pB; Set::const_iterator pIt; bool pInB; }; static_assert(std::forward_iterator<Iterator>); static_assert(std::ranges::input_range<MyFacadeClass>); public: Iterator begin() const { return Iterator(self, &pA, &pB, pA.begin()); } Iterator end() const { return Iterator(self, &pA, &pB, pB.end()); } // 构造函数,初始化成员 MyFacadeClass(std::ptrdiff_t self_val, const Set& a, const Set& b) : self(self_val), pA(a), pB(b) {} private: const std::ptrdiff_t self; const Set& pA; const Set& pB; };
使用示例
现在可以正常使用range适配器:
#include <ranges> #include <iostream> int main() { Set a{1, 2, 3, 4}; Set b{3, 4, 5, 6}; const MyFacadeClass my_class(2, a, b); // 使用views::filter过滤成员 for (const auto& member : my_class | std::views::filter([](auto val) { return val % 2 == 0; })) { std::cout << member << " "; } // 输出:4 4 6 return 0; }
内容的提问来源于stack exchange,提问作者user-1

