关于c ++:对std :: vector的迭代:unsigned vs signed index变量

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];
}

生成warning: comparison between signed and unsigned integer expressions

在C++世界中,我是新的,所以EDCOX1×1变量对我来说有点吓人,而且我知道如果不正确使用EDCOX1,1变量可能是危险的,那么这是正确的吗?


向后迭代

看看这个答案。

向前迭代

这几乎是相同的。只需按增量更改迭代器/交换减量。您应该更喜欢迭代器。有些人告诉您使用std::size_t作为索引变量类型。但是,这是不可移植的。始终使用容器的size_typetypedef(虽然在前向迭代情况下您可以只进行转换,但在后向迭代情况下,当使用std::size_t时,如果std::size_tsize_type的typedef宽,它实际上可能会一直出错):

使用std::vector使用迭代器

1
2
3
for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    /* std::cout << *it; ... */
}

重要的是,对于您不知道其定义的迭代器,始终使用前缀增量形式。这将确保您的代码尽可能通用。

使用范围C++ 11

1
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]; ... */
}

但是,请阅读后向迭代的答案,sizeof方法可以解决什么问题。


四年过去了,谷歌给了我这个答案。使用标准C++ 11(AKC+0x),实际上有一种新的令人愉快的方法(以打破向后兼容性为代价):新的EDCOX1×0 }关键字。当迭代器的类型很明显(对编译器来说)要使用哪种类型时,它可以避免必须显式地指定要使用的迭代器的类型(再次重复向量类型)的痛苦。当v是你的vector时,你可以这样做:

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;
}

要在工作程序中查看它,请构建一个文件auto.cpp

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) {
...


调用vector::size()返回std::vector::size_type类型的值,而不是int、unsigned int或其他类型的值。

通常,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是存储在向量中的数据类型。

或者使用不同的迭代算法(std::transformstd::copystd::fillstd::for_each等)。


使用size_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 type size_t, and the sizeof operator evaluates to size_t.

The actual type of size_t is platform-dependent; a common mistake is to assume size_t is the same as unsigned int, which can lead to programming errors, particularly as 64-bit architectures become more prevalent.


有点历史:

要表示一个数字是否为负数,请使用"符号"位。int是一种有符号的数据类型,意味着它可以保存正值和负值(约为-20亿到20亿)。Unsigned只能存储正数(而且由于它不会在元数据上浪费一点时间,所以可以存储更多:0到40亿)。

std::vector::size()返回一个Unsigned,因为一个向量怎么可能有负的长度?

警告说明不等式语句的右操作数可以容纳比左操作数更多的数据。

基本上,如果有一个向量有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中

我将使用像for_each这样的通用算法来避免搜索正确类型的迭代器和lambda表达式来避免额外命名的函数/对象。

针对特定情况的简短"漂亮"示例(假设多边形是整数的向量):

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。注意,您不能使用此技术来更改向量的元素。