C++11/14 新特性(for循环)
在C++ 中,遍历一个容器的方法一般是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> #include <vector> int main(void) { std::vector<int> vec; //do sth std::vector<int>::iterator it = vec.begin(); for(; it != vec.end(); ++it) { std::cout<< *it <<std::endl; } return 0; } |
对STL比较熟悉的程序员肯定还知道在 <algorithm> 中有一个 for_each 算法,可以用来完成上述功能:
1 2 3 4 5 6 7 |
int main(void) { std::vector<int> vec; //do sth std::for_each(vec.begin(), vec.end(), [](auto val){std::cout<<val<<std::endl;}); return 0; } |
这里借助了 auto 关键字和 lambda 表达式简化了操作。 std::for_each 比起前面 for 循环,最大的好处是不再需要关注迭代器的概念,而只需要关心容器中的元素类型即可。 但这两种方法,都必须显式的给出容器有开头和结尾(begin,end).这是因为上面的两种方法都不是基于范围(range)来设计的. 范围的概念在很多高级语言中都有涉及。例如下面这段 python 代码:
1 2 3 |
arr = [1,2,3,4,5] for n in arr: print n |
在这种循环中,不再需要关心容器的两端,循环会自动以容器的范围进行展开。而且这种语法可以清楚地表明它的意义。使用这种方式进行循环无疑会使编码和维护更加简便。 现在,c++11 可有了基于范围的 for 循环:
1 2 3 4 5 6 7 8 9 10 |
int main(void) { std::vector<int> vec = {1,2,3}; //do sth for(auto n : vec) { std::cout<< n << std::endl; } return 0; } |
在这段代码中,n 是容器 vec 中的一个元素,而 auto 则使用编译器自动推导出 n 的类型。n 之后使用冒号与需要遍历的容器连接,for 将自动在容器的范围内进行迭代。 也可以不使用 auto 关键字,而使用明确的类型来定义局部变量 n ,这里还支持对 n 进行隐式类型转换。 在上面的例子中,我们都是进行只读方式的遍历。如果需要在遍历的同时修改元素,则需要使用引用 :
1 2 3 4 5 6 7 8 9 10 |
int main(void) { std::vector<int> vec = {1,2,3}; //do sth for(auto& n : vec) { std::cout<< n++ << std::endl; } return 0; } |
当然,如果 n 是复杂类型,而我们又不需要对容器元素进行个性,还可以使用 const auto& n 来定义,这样可以提高效率。 需要注意的是,这种用法不是对所有的容器都有效。例如,对于 std::set ,其元素是只读的,不可以使用引用操作符对其进行修改。 而对于 std::map,返回的是 pair ,而不是 iterator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
std::set<int> st = { 1,2,3,4,5 }; for (auto& n : st) { n++; //ERROR 不可修改的左值 } std::map<char, int> mp= { {'a',1}, {'b':2}, {'c',3} }; for (auto m : mp) { std::cout << m.first << "," << m.second << std::endl; } |