C++ 增强for循环(for each)

1,210 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

在C++11后,增加了一个新的东西,是针对于for循环而言的。

是一种基于迭代器的for each循环的样子

在其他语言中可能早就有这个玩意了,而在C++11之前,这个语法糖并没有,反而是在库中有一个这样的东西。

其类似于这种:

int main() {
  std::vector<int> ve{1, 2, 3, 4};
  {
    struct out {
      void operator()(const int &x) { std::cout << x << " "; }
    };
    std::for_each(std::begin(ve), std::end(ve), out());
  }
  return 0;
}

利用仿函数和迭代器结合来完成遍历这一件事情的。

为什么要这样搞呢,因为在c++98用迭代器访问可能有点麻烦 (

std::vector<int> ve{1, 2, 3, 4};
{
  for (std::vector<int>::iterator it = ve.begin(); it != ve.end(); ++it) {
    std::cout << *it << " ";
  }
}

上面那个迭代器还是一个简单的迭代器,若是碰上元素比较复杂的情况,还是要写上许多累赘代码的。

c++ 一直贯穿可以加入标准库的就先不要当作特性来搞。

所以,c++98中 for_each 作为一个库函数登场了

但是,c++98中没有lambda表达式,要通过仿函数的方式来处理遍历的对象。

于是,还是比较繁琐,于是,在c++11中,增强for循环这个语法糖来解决这个问题了。


c++ 的 增强for循环 形如这种:

for (变量类型 变量名 : 容器) {
 ... 处理
}

和python、Java的foreach类似。

其实Java的foreach也相当于是一个语法糖嘛。

那这个语法糖是如何实现的呢?

和c++98的for_each一样,是通过 它的迭代器来达到这一目的。

至于为什么可以自动推导迭代器,用的还是类型萃取那一套的技巧。

增强for循环至少要满足如下条件:

容器类型有 begin() end() 这两个成员函数 或者 begin() end()的函数的重载

容器返回一个迭代器,这个迭代器至少满足 重载了 operator!=, operator*, operator++ 三个运算符

为什么要满足如上条件才能用呢?

证明增强for循环是语法糖的最好依据

  for (std::vector<int>::iterator it = ve.begin(); it != ve.end(); ++it) {
    std::cout << *it << " ";
  }

比如我们可以自定义一个range,让其满足基本的使用条件。

struct range {
private:
  int start_, end_;
  struct iter {
    int val;
    iter(int x) : val(x) {}
    int operator*() const { return val; }
    bool operator!=(iter rhs) const { return val != rhs.val; }
    iter operator++() { return val++, *this; }
  };

public:
  using range_iter = range::iter;
  range(int start, int end) : start_(start), end_(end) {}
  range_iter begin() const { return start_; }
  range_iter end() const { return end_; }
};

根据以上range类而来的迭代

for (int v : range(1, 4)) {
    std::cout << v << " ";
}

对应着下列的语法糖


{
range && __range1 = range(1, 4);
range::iter __begin1 = __range1.begin();
range::iter __end1 = __range1.end();
for(; __begin1.operator!=(range::iter(__end1)); __begin1.operator++()) {
  int v = __begin1.operator*();
  std::operator<<(std::cout.operator<<(v), " ");
}
}