c++11之后有了 range for 语法,那么那么对于数字的 for,可不可以也用 range for 来写呢。这就要理解 c++11 的 range for 具体是怎么工作的。实际上,c++的 range for 语句,在编译时会被展开,比如这句话
for(auto &p : obj)
实际上会被展开为:
for(auto it=obj.begin(); it!=obj.end(); ++it)
而 range for 中得到的 p 实际上是的 it 的解引用,也就是 p=*it。
从上面可以看到,对于一个obj,其可以使用 range for 的条件是,obj 有至少有两个函数,obj.begin() 和 obj.end()。这两个函数返回相同的类型,这个返回类型我们称为 obj 的迭代器。除了 obj 满足有这两个函数之外,还要求 obj 的迭代器满足下列三个条件
- 有前置++算符,(展开中的 ++it 要用)
- 有不等于判断算符,(终止条件是用不等于算符判断的)
- 有解应用算符,因为 range for 得到的是迭代器的解引用
了解了以上几点,我们就可以写一个自己的 Range 类,使其可以实现整数的 range。
先实现 Range 的迭代器:
class Range_iterator{
public:
Range_iterator(std::size_t c) : count(c) {}
~Range_iterator() = default;
Range_iterator &operator++() {++count; return *this;}
bool operator!=(const Range_iterator &ri) {return count != ri.count;}
std::size_t &operator*() {return count;}
private:
std::size_t count;
};
其中特别注意的一点是,解引用算符返回的必须是引用,因为解引用通常都是左值。
接下来就是 Range 了:
class Range{
public:
Range(std::size_t b, std::size_t e) : Begin(b),End(e) {if(b > e) Begin = End;}
Range(std::size_t e) : Range(0, e) {}
~Range() = default;
Range_iterator begin(){return Range_iterator(Begin);}
Range_iterator end(){return Range_iterator(End);}
private:
std::size_t Begin;
std::size_t End;
};
意外的很简单,我们必须让 Range 记住开始和结尾,迭代过程的自加则交给 Range 的迭代器。
我这里使用 size_t 而不是 int,是因为一般而言,使用 range 还是为了索引,而 c++ 不同于python,是没有负数索引的,而且标准库的 size 一般都是使用等价于 size_t 的类型来表示。
下面测试一下怎么用:
using range = Range;
int main(){
for(auto &i : range(20)) std::cout << i << ' ';
std::cout << '\n';
std::string s = "Hello World!";
for(auto &i : range(10)) std::cout << s[i] << ' ';
}
这个代码的运行结果是:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
H e l l o W o r l
是不是瞬间感觉清爽了很多。当然这个用法还是其次,这个问题对于理解 c++ 的 range for 还是很有帮助的,我觉得这个代码应该是可以实现 range for 的自定义类型中最简单的了。