Iteration over std::vector: unsigned vs signed index variable
在C++中迭代向量的正确方法是什么?
考虑这两个代码片段,这一个很好:
1 2 3 | for (unsigned i=0; i < polygon.size(); i++) { sum += polygon[i]; } |
这一个:
1 2 3 | for (int i=0; i < polygon.size(); i++) { sum += polygon[i]; } |
生成
在C++世界中,我是新的,所以EDCOX1×1变量对我来说有点吓人,而且我知道如果不正确使用EDCOX1,1变量可能是危险的,那么这是正确的吗?
向后迭代
看看这个答案。
向前迭代这几乎是相同的。只需按增量更改迭代器/交换减量。您应该更喜欢迭代器。有些人告诉您使用
1 2 3 | for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) { /* std::cout << *it; ... */ } |
重要的是,对于您不知道其定义的迭代器,始终使用前缀增量形式。这将确保您的代码尽可能通用。
使用范围C++ 111 2 | for(auto const& value: a) { /* std::cout << value; ... */ |
使用指数
1 2 3 | for(std::vector<int>::size_type i = 0; i != v.size(); i++) { /* std::cout << v[i]; ... */ } |
使用数组使用迭代器
1 2 3 | for(element_type* it = a; it != (a + (sizeof a / sizeof *a)); it++) { /* std::cout << *it; ... */ } |
使用C++ 11范围
1 2 | for(auto const& value: a) { /* std::cout << value; ... */ |
使用指数
1 2 3 | for(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) { /* std::cout << a[i]; ... */ } |
但是,请阅读后向迭代的答案,
四年过去了,谷歌给了我这个答案。使用标准C++ 11(AKC+0x),实际上有一种新的令人愉快的方法(以打破向后兼容性为代价):新的EDCOX1×0 }关键字。当迭代器的类型很明显(对编译器来说)要使用哪种类型时,它可以避免必须显式地指定要使用的迭代器的类型(再次重复向量类型)的痛苦。当
1 2 3 | for ( auto i = v.begin(); i != v.end(); i++ ) { std::cout << *i << std::endl; } |
C++ 11更进一步,并给出了一个特殊的语法,用于迭代像向量这样的集合。它消除了写总是相同的东西的必要性:
1 2 3 | for ( auto &i : v ) { std::cout << i << std::endl; } |
要在工作程序中查看它,请构建一个文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <vector> #include <iostream> int main(void) { std::vector<int> v = std::vector<int>(); v.push_back(17); v.push_back(12); v.push_back(23); v.push_back(42); for ( auto &i : v ) { std::cout << i << std::endl; } return 0; } |
在编写这篇文章时,当您使用g++编译这篇文章时,通常需要通过提供一个额外的标志将其设置为使用新标准:
1 | g++ -std=c++0x -o auto auto.cpp |
现在可以运行以下示例:
1 2 3 4 5 | $ ./auto 17 12 23 42 |
请注意,编译和运行的指令是特定于Linux上的GNU C++编译器,程序应该是平台(和编译器)独立的。
在您的示例中的特定情况下,我将使用STL算法来实现这一点。
1 2 3 | #include <numeric> sum = std::accumulate( polygon.begin(), polygon.end(), 0 ); |
对于更一般但仍然相当简单的情况,我会选择:
1 2 3 4 5 | #include <boost/lambda/lambda.hpp> #include <boost/lambda/bind.hpp> using namespace boost::lambda; std::for_each( polygon.begin(), polygon.end(), sum += _1 ); |
关于Johannes Schaub的回答:
1 2 3 | for(std::vector<T*>::iterator it = v.begin(); it != v.end(); ++it) { ... } |
这可能适用于某些编译器,但不适用于gcc。这里的问题是,如果std::vector::iterator是一个类型、变量(成员)或函数(方法),那么问题就是。GCC出现以下错误:
1 2 3 4 5 6 7 8 | In member function ‘void MyClass<T>::myMethod()’: error: expected `;' before ‘it’ error: ‘it’ was not declared in this scope In member function ‘void MyClass<T>::sort() [with T = MyClass]’: instantiated from ‘void MyClass<T>::run() [with T = MyClass]’ instantiated from here dependent-name ‘std::vector<T*,std::allocator<T*> >::iterator’ is parsed as a non-type, but instantiation yields a type note: say ‘typename std::vector<T*,std::allocator<T*> >::iterator’ if a type is meant |
解决方案是使用关键字"typename",如前所述:
1 2 3 | typename std::vector<T*>::iterator it = v.begin(); for( ; it != v.end(); ++it) { ... |
调用
通常,C++中容器上的迭代是使用迭代器完成的,像这样。
1 2 3 4 5 6 | std::vector<T>::iterator i = polygon.begin(); std::vector<T>::iterator end = polygon.end(); for(; i != end; i++){ sum += *i; } |
其中t是存储在向量中的数据类型。
或者使用不同的迭代算法(
使用
1 | for (size_t i=0; i < polygon.size(); i++) |
引用维基百科:
The stdlib.h and stddef.h header files define a datatype called
size_t which is used to represent the size of an object. Library functions that take sizes expect them to be of typesize_t , and the sizeof operator evaluates tosize_t .The actual type of
size_t is platform-dependent; a common mistake is to assumesize_t is the same as unsigned int, which can lead to programming errors, particularly as 64-bit architectures become more prevalent.
有点历史:
要表示一个数字是否为负数,请使用"符号"位。
警告说明不等式语句的右操作数可以容纳比左操作数更多的数据。
基本上,如果有一个向量有20亿个以上的条目,并且使用一个整数对其进行索引,那么就会遇到溢出问题(int值将回绕到负20亿左右)。
我通常使用Boost-ForEach:
1 2 3 4 5 | #include <boost/foreach.hpp> BOOST_FOREACH( vector_type::value_type& value, v ) { // do something with 'value' } |
它适用于STL容器、数组、C样式字符串等。
在C++ 11中
我将使用像
针对特定情况的简短"漂亮"示例(假设多边形是整数的向量):
1 | for_each(polygon.begin(), polygon.end(), [&sum](int i){ sum += i; }); |
测试日期:http://ideone.com/i6ethd
别忘了包括:算法,当然还有向量:)
微软实际上也有一个很好的例子:来源:http://msdn.microsoft.com/en-us/library/dd293608.aspx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include #include <iostream> #include <vector> using namespace std; int main() { // Create a vector object that contains 10 elements. vector<int> v; for (int i = 1; i < 10; ++i) { v.push_back(i); } // Count the number of even numbers in the vector by // using the for_each function and a lambda. int evenCount = 0; for_each(v.begin(), v.end(), [&evenCount] (int n) { cout << n; if (n % 2 == 0) { cout <<" is even" << endl; ++evenCount; } else { cout <<" is odd" << endl; } }); // Print the count of even numbers to the console. cout <<"There are" << evenCount <<" even numbers in the vector." << endl; } |
若要完成,C++ 11语法仅允许迭代器(REF)的另一个版本:
1 2 3 | for(auto it=std::begin(polygon); it!=std::end(polygon); ++it) { // do something with *it } |
这对于逆向迭代也很舒服
1 2 3 | for(auto it=std::end(polygon)-1; it!=std::begin(polygon)-1; --it) { // do something with *it } |
1 2 | for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++) sum += *it; |
考虑是否需要迭代
1 2 3 | using std::begin; // allows argument-dependent lookup even using std::end; // if the container type is unknown here auto sum = std::accumulate(begin(polygon), end(polygon), 0); |
算法库中的其他函数执行常见任务-如果您想节省精力,请确保您知道哪些功能可用。
第一个是类型正确,在某种严格意义上是正确的。(如果你认为是的话,大小永远不能小于零。)不过,这个警告让我觉得是被忽视的好候选人之一。
这两个代码段工作原理相同。但是,unsigned int"路由是正确的。使用无符号int类型将更好地处理使用它的实例中的向量。对向量调用size()成员函数将返回一个无符号整数值,因此您需要将变量"i"与其自身类型的值进行比较。
此外,如果您对"unsigned int"在代码中的外观仍有点不安,请尝试"uint"。这基本上是"unsigned int"的缩写版本,其工作原理完全相同。您也不需要包括其他头来使用它。
模糊但重要的细节:如果您按如下方式说"for(auto it)",则会得到对象的副本,而不是实际元素的副本:
1 2 3 4 5 6 | struct Xs{int i} x; x.i = 0; vector <Xs> v; v.push_back(x); for(auto it : v) it.i = 1; // doesn't change the element v[0] |
要修改向量的元素,需要将迭代器定义为引用:
1 | for(auto &it : v) |
如果编译器支持,则可以使用基于范围的来访问向量元素:
1 2 3 4 5 | vector<float> vertices{ 1.0, 2.0, 3.0 }; for(float vertex: vertices){ std::cout << vertex <<""; } |
印刷品:1 2 3。注意,您不能使用此技术来更改向量的元素。