iota_view随机访问特性为何依赖元素类型?如何让自定义Foo支持?
自定义类型iota_view随机访问失败的原因与解决方法
问题描述
以下代码中,取消注释std::cout << foos[15] << std::endl;后编译失败,需要分析原因并给出std-ranges下的修复方案,同时对比range-v3的实现差异:
#include <iostream> #include <ranges> struct Foo { int x; Foo() : x{-1} {} Foo(int x) : x{x} {} using difference_type = int; friend auto operator<=>(Foo const& a, Foo const& b) { return a.x <=> b.x; } friend bool operator==(Foo const& a, Foo const& b) { return a.x == b.x; } auto& operator++() { ++x; return *this; } auto operator++(int) { auto r = *this; ++x; return r; } }; int main () { auto ints = std::views::iota(1, 30); for (auto i : ints) {} std::cout << ints[15] << std::endl; auto foos = std::views::iota(Foo{1}, Foo{30}); for (auto i : foos) {} //std::cout << foos[15] << std::endl; }
编译失败原因
std::views::iota生成的视图要支持operator[](随机访问),要求元素类型满足随机访问迭代器的配套要求:除了自增操作,还必须支持与difference_type的加减运算、以及元素间的差值计算。
当前Foo仅实现了自增操作,但没有定义operator+(接受difference_type)、operator-、operator+=、operator-=等必要运算符,编译器无法直接从起始的Foo{1}计算出第15个元素(需要直接跳转而非逐次递增),因此iota_view<Foo, Foo>无法满足random_access_range概念,自然不支持operator[]。
std-ranges下的修复方案
给Foo补充随机访问所需的运算符,让类型支持直接的偏移计算:
#include <iostream> #include <ranges> struct Foo { int x; Foo() : x{-1} {} Foo(int x) : x{x} {} using difference_type = int; friend auto operator<=>(Foo const& a, Foo const& b) { return a.x <=> b.x; } friend bool operator==(Foo const& a, Foo const& b) { return a.x == b.x; } // 已有自增实现 auto& operator++() { ++x; return *this; } auto operator++(int) { auto r = *this; ++x; return r; } // 补充随机访问必需的运算符 Foo operator+(difference_type n) const { return Foo(x + n); } Foo& operator+=(difference_type n) { x += n; return *this; } Foo operator-(difference_type n) const { return Foo(x - n); } Foo& operator-=(difference_type n) { x -= n; return *this; } difference_type operator-(Foo const& other) const { return x - other.x; } }; int main () { auto ints = std::views::iota(1, 30); for (auto i : ints) {} std::cout << ints[15] << std::endl; auto foos = std::views::iota(Foo{1}, Foo{30}); for (auto i : foos) {} std::cout << foos[15].x << std::endl; // 编译通过,输出16 }
补充这些运算符后,std::iota_view就能满足random_access_range的要求:它可以通过起始元素直接加上偏移量得到目标元素,无需逐次递增,因此operator[]可以正常工作。
range-v3与std-ranges的差异
两者核心逻辑一致,但存在细节区别:
- range-v3的
iota_view更依赖于ranges::advance的自定义适配,但本质上仍要求类型支持与difference_type的加减运算才能实现随机访问。 - range-v3的概念检查更早更严格,如果类型不满足随机访问要求,会直接拒绝
operator[]的调用;而std-ranges的SFINAE机制可能在错误提示上更分散,但本质限制相同。 - 仅实现自增操作时,两者的
iota_view都只能作为前向范围,无法支持随机访问,也就不能使用operator[]。
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

