C++20自定义容器迭代器无法适配std::ranges算法问题排查
我编写了如下简化代码,实现了一个整数范围容器,希望配合各类std标准算法使用。目前我正尝试升级代码以使用C++20的ranges版本算法,从而省略所有显式的begin()和end()调用。在下方代码中,std::any_of可正常配合我的自定义容器和迭代器运行,但std::ranges::any_of却无法通过编译。
#include <iostream> #include <algorithm> class Number_Iterator { public: using iterator_category = std::input_iterator_tag; using value_type = int; using difference_type = int; using pointer = int*; using reference = int&; Number_Iterator(int start) noexcept : value(start) {} Number_Iterator& operator++() noexcept { ++value; return *this; } bool operator==(const Number_Iterator& other) const noexcept = default; int operator*() const noexcept { return value; } private: int value; }; class Numbers { public: Numbers(int begin, int end) noexcept : begin_value(begin), end_value(end) {} Number_Iterator begin() const noexcept { return {begin_value}; } Number_Iterator end() const noexcept { return {end_value}; } private: int begin_value; int end_value; }; int main() { const auto set = Numbers(1, 10); const auto multiple_of_three = [](const auto n) { return n % 3 == 0; }; // Compiles and runs correctly if(std::any_of(set.begin(), set.end(), multiple_of_three)) { std::cout << "Contains multiple of three.\n"; } // Does not compile if(std::ranges::any_of(set, multiple_of_three)) { std::cout << "Contains multiple of three.\n"; } return 0; }
我在开启/std:c++20编译选项的Visual Studio 2019(16.11.15版本)中编译上述代码,得到如下报错信息:
Source.cpp(42,21): error C2672: 'operator __surrogate_func': no matching overloaded function found Source.cpp(42,7): error C7602: 'std::ranges::_Any_of_fn::operator ()': the associated constraints are not satisfied 1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include\algorithm(1191): message : see declaration of 'std::ranges::_Any_of_fn::operator ()'
我尝试查阅std::ranges::_Any_of_fn::operator()的声明定义,反而对此更加困惑。
请问我还需要补充哪些实现,才能让std::ranges系列算法正常适配我的自定义容器?
补充说明:我实际需要迭代的对象是国际象棋棋盘上的格子,这类对象通过整数表示,和上述示例代码的逻辑差异极小。
你的自定义迭代器不符合C++20 ranges对输入迭代器的概念约束。传统std::any_of没有严格的编译期概念校验,只要调用的接口存在就能编译运行,但ranges算法会在编译期严格校验迭代器是否满足对应概念要求,不满足就会直接报约束不满足的错误。核心问题有两个:
- 缺失后置自增运算符
operator++(int):C++20输入迭代器概念明确要求同时实现前置、后置自增,你的迭代器只实现了前置版本。 - 类型定义与实际行为不匹配:你的
operator*直接返回int纯右值,但内部定义的reference类型是int&,二者不一致,导致迭代器的可间接读取约束校验失败。
方案1:修正迭代器定义,适配ranges概念要求
针对你的迭代器场景(解引用返回整数纯右值,不需要修改元素),补全缺失接口、修正错误的类型定义即可,不需要修改Numbers容器的代码:
class Number_Iterator { public: // C++20迭代器通过iterator_concept标识迭代器类别 using iterator_concept = std::input_iterator_tag; using value_type = int; using difference_type = int; // 解引用返回int纯右值,reference不要定义为不存在的int& using reference = int; using pointer = const int*; Number_Iterator(int start) noexcept : value(start) {} // 前置自增 Number_Iterator& operator++() noexcept { ++value; return *this; } // 补全ranges要求的后置自增 Number_Iterator operator++(int) noexcept { auto temp = *this; ++value; return temp; } bool operator==(const Number_Iterator& other) const noexcept = default; int operator*() const noexcept { return value; } private: int value; };
修改完成后std::ranges::any_of即可正常编译运行。你实际场景中的棋盘格迭代器如果也是解引用返回值类型,按同样逻辑修改即可。
方案2:直接使用标准库提供的范围工具,无需自定义迭代器
你的需求本质是遍历连续整数区间,C++20已经提供了现成的std::views::iota视图可以直接实现相同功能,不需要自行编写迭代器和容器:
#include <iostream> #include <algorithm> #include <ranges> int main() { // 生成[1,10)的整数序列视图 auto set = std::views::iota(1, 10); const auto multiple_of_three = [](const auto n) { return n % 3 == 0; }; if(std::ranges::any_of(set, multiple_of_three)) { std::cout << "Contains multiple of three.\n"; } return 0; }
内容的提问来源于stack exchange,提问作者Mark H

