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比较熟悉的程序员肯定还知道在 中有一个 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;
}