std::vector 的迭代:无符号与有符号索引变量

发布于 2024-07-10 22:04:48 字数 402 浏览 7 评论 0原文

在 C++ 中迭代向量的正确方法是什么?

考虑这两个代码片段,这个工作正常:

for (unsigned i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

而这个:

for (int i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

生成警告:有符号和无符号整数表达式之间的比较

unsigned 变量对我来说看起来有点可怕,我知道 unsigned 变量如果使用不当可能会很危险,所以 - 这是正确的吗?

What is the correct way of iterating over a vector in C++?

Consider these two code fragments, this one works fine:

for (unsigned i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

and this one:

for (int i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

which generates warning: comparison between signed and unsigned integer expressions.

The unsigned variable looks a bit frightening to me and I know unsigned variables can be dangerous if not used correctly, so - is this correct?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

绝不放开 2024-07-17 22:04:48

对于向后迭代,请参阅此答案。

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


使用 std::vector

使用迭代器

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

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

使用 Range C++11

for(auto const& value: a) {
     /* std::cout << value; ... */

使用索引

for(std::vector<int>::size_type i = 0; i != v.size(); i++) {
    /* std::cout << v[i]; ... */
}

使用数组

使用迭代器

for(element_type* it = a; it != (a + (sizeof a / sizeof *a)); it++) {
    /* std::cout << *it; ... */
}

使用 Range C++11

for(auto const& value: a) {
     /* std::cout << value; ... */

使用索引

for(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) {
    /* std::cout << a[i]; ... */
}

阅读向后迭代的答案,但是 sizeof 方法可能会产生什么问题。

For iterating backwards see this answer.

Iterating forwards is almost identical. Just change the iterators / swap decrement by increment. You should prefer iterators. Some people tell you to use std::size_t as the index variable type. However, that is not portable. Always use the size_type typedef of the container (While you could get away with only a conversion in the forward iterating case, it could actually go wrong all the way in the backward iterating case when using std::size_t, in case std::size_t is wider than what is the typedef of size_type):


Using std::vector

Using iterators

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

Important is, always use the prefix increment form for iterators whose definitions you don't know. That will ensure your code runs as generic as possible.

Using Range C++11

for(auto const& value: a) {
     /* std::cout << value; ... */

Using indices

for(std::vector<int>::size_type i = 0; i != v.size(); i++) {
    /* std::cout << v[i]; ... */
}

Using arrays

Using iterators

for(element_type* it = a; it != (a + (sizeof a / sizeof *a)); it++) {
    /* std::cout << *it; ... */
}

Using Range C++11

for(auto const& value: a) {
     /* std::cout << value; ... */

Using indices

for(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) {
    /* std::cout << a[i]; ... */
}

Read in the backward iterating answer what problem the sizeof approach can yield to, though.

戏蝶舞 2024-07-17 22:04:48

四年过去了,Google给了我这个答案。 使用标准C++11(又名C++0x )实际上有一种新的令人愉快的方法可以做到这一点(以破坏向后兼容性为代价):新的 auto 关键字。 当使用哪种类型(对编译器而言)显而易见时,它可以让您免去必须显式指定要使用的迭代器类型(再次重复向量类型)的痛苦。 使用 v 作为您的向量,您可以执行以下操作:

for ( auto i = v.begin(); i != v.end(); i++ ) {
    std::cout << *i << std::endl;
}

C++11 更进一步,为您提供了一种用于迭代的特殊语法像向量一样的集合。 它消除了编写始终相同的内容的必要性:

for ( auto &i : v ) {
    std::cout << i << std::endl;
}

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

#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++ 编译它时/em>,您通常需要通过提供额外的标志来将其设置为与新标准一起使用:

g++ -std=c++0x -o auto auto.cpp

现在您可以运行该示例:

$ ./auto
17
12
23
42

请注意有关编译和运行的说明特定于 Linux 上的 >gnu c++ 编译器,程序应该独立于平台(和编译器)。

Four years passed, Google gave me this answer. With the standard C++11 (aka C++0x) there is actually a new pleasant way of doing this (at the price of breaking backward compatibility): the new auto keyword. It saves you the pain of having to explicitly specify the type of the iterator to use (repeating the vector type again), when it is obvious (to the compiler), which type to use. With v being your vector, you can do something like this:

for ( auto i = v.begin(); i != v.end(); i++ ) {
    std::cout << *i << std::endl;
}

C++11 goes even further and gives you a special syntax for iterating over collections like vectors. It removes the necessity of writing things that are always the same:

for ( auto &i : v ) {
    std::cout << i << std::endl;
}

To see it in a working program, build a file auto.cpp:

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

As of writing this, when you compile this with g++, you normally need to set it to work with the new standard by giving an extra flag:

g++ -std=c++0x -o auto auto.cpp

Now you can run the example:

$ ./auto
17
12
23
42

Please note that the instructions on compiling and running are specific to gnu c++ compiler on Linux, the program should be platform (and compiler) independent.

蒗幽 2024-07-17 22:04:48

在您的示例中的特定情况下,我将使用 STL 算法来完成此任务。

#include <numeric> 

sum = std::accumulate( polygon.begin(), polygon.end(), 0 );

对于更一般但仍然相当简单的情况,我会选择:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace boost::lambda;
std::for_each( polygon.begin(), polygon.end(), sum += _1 );

In the specific case in your example, I'd use the STL algorithms to accomplish this.

#include <numeric> 

sum = std::accumulate( polygon.begin(), polygon.end(), 0 );

For a more general, but still fairly simple case, I'd go with:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace boost::lambda;
std::for_each( polygon.begin(), polygon.end(), sum += _1 );
缪败 2024-07-17 22:04:48

关于 Johannes Schaub 的回答:

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

这可能适用于某些编译器,但不适用于 gcc。 这里的问题是 std::vector::iterator 是类型、变量(成员)还是函数(方法)的问题。 我们在 gcc 中遇到以下错误:

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”,如下所示:

typename std::vector<T*>::iterator it = v.begin();
for( ; it != v.end(); ++it) {
...

Regarding Johannes Schaub's answer:

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

That may work with some compilers but not with gcc. The problem here is the question if std::vector::iterator is a type, a variable (member) or a function (method). We get the following error with gcc:

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

The solution is using the keyword 'typename' as told:

typename std::vector<T*>::iterator it = v.begin();
for( ; it != v.end(); ++it) {
...
挽清梦 2024-07-17 22:04:48

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

通常,C++ 中的容器迭代是使用迭代器完成的,如下所示。

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 等等)。

A call to vector<T>::size() returns a value of type std::vector<T>::size_type, not int, unsigned int or otherwise.

Also generally iteration over a container in C++ is done using iterators, like this.

std::vector<T>::iterator i = polygon.begin();
std::vector<T>::iterator end = polygon.end();

for(; i != end; i++){
    sum += *i;
}

Where T is the type of data you store in the vector.

Or using the different iteration algorithms (std::transform, std::copy, std::fill, std::for_each et cetera).

太阳男子 2024-07-17 22:04:48

使用 size_t

for (size_t i=0; i < polygon.size(); i++)

引用 Wikipedia

stdlib.h 和 stddef.h 头文件定义了一个名为 size_t 的数据类型,用于表示对象的大小。 接受大小的库函数期望它们的类型为 size_t,并且 sizeof 运算符的计算结果为 size_t

size_t 的实际类型与平台相关; 一个常见的错误是假设 size_t 与 unsigned int 相同,这可能会导致编程错误,尤其是在 64 位架构变得更加流行的情况下。

Use size_t :

for (size_t i=0; i < polygon.size(); i++)

Quoting Wikipedia:

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.

爱冒险 2024-07-17 22:04:48

一点历史:

为了表示一个数字是否为负数,计算机使用“符号”位。 int 是一种有符号数据类型,这意味着它可以保存正值和负值(大约 -20 亿到 20 亿)。 Unsigned 只能存储正数(并且由于它不会在元数据上浪费一点,所以可以存储更多:0 到大约 40 亿)。

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

该警告告诉您,不等式语句的右操作数可以容纳比左操作数更多的数据。

本质上,如果您有一个包含超过 20 亿个条目的向量,并且您使用整数进行索引,则会遇到溢出问题(整数将回绕到负 20 亿)。

A bit of history:

To represent whether a number is negative or not computer use a 'sign' bit. int is a signed data type meaning it can hold positive and negative values (about -2billion to 2billion). Unsigned can only store positive numbers (and since it doesn't waste a bit on metadata it can store more: 0 to about 4billion).

std::vector::size() returns an unsigned, for how could a vector have negative length?

The warning is telling you that the right operand of your inequality statement can hold more data then the left.

Essentially if you have a vector with more then 2 billion entries and you use an integer to index into you'll hit overflow problems (the int will wrap back around to negative 2 billion).

像极了他 2024-07-17 22:04:48

我通常使用 BOOST_FOREACH:

#include <boost/foreach.hpp>

BOOST_FOREACH( vector_type::value_type& value, v ) {
    // do something with 'value'
}

它适用于 STL 容器、数组、C 风格字符串等。

I usually use BOOST_FOREACH:

#include <boost/foreach.hpp>

BOOST_FOREACH( vector_type::value_type& value, v ) {
    // do something with 'value'
}

It works on STL containers, arrays, C-style strings, etc.

暖树树初阳… 2024-07-17 22:04:48

为了完整起见,C++11 语法仅启用迭代器的另一个版本 (ref ):

for(auto it=std::begin(polygon); it!=std::end(polygon); ++it) {
  // do something with *it
}

这也适合反向迭代

for(auto it=std::end(polygon)-1; it!=std::begin(polygon)-1; --it) {
  // do something with *it
}

To be complete, C++11 syntax enables just one another version for iterators (ref):

for(auto it=std::begin(polygon); it!=std::end(polygon); ++it) {
  // do something with *it
}

Which is also comfortable for reverse iteration

for(auto it=std::end(polygon)-1; it!=std::begin(polygon)-1; --it) {
  // do something with *it
}
梦行七里 2024-07-17 22:04:48

在 C++11 中,

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

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

for_each(polygon.begin(), polygon.end(), [&sum](int i){ sum += i; });

测试于:http:// /ideone.com/i6Ethd

不要忘记包括:算法,当然还有向量:)

微软实际上在这方面也有一个很好的例子:

来源:http://msdn.microsoft.com/en -us/library/dd293608.aspx

#include <algorithm>
#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;
}

In C++11

I would use general algorithms like for_each to avoid searching for the right type of iterator and lambda expression to avoid extra named functions/objects.

The short "pretty" example for your particular case (assuming polygon is a vector of integers):

for_each(polygon.begin(), polygon.end(), [&sum](int i){ sum += i; });

tested on: http://ideone.com/i6Ethd

Dont' forget to include: algorithm and, of course, vector :)

Microsoft has actually also a nice example on this:

source: http://msdn.microsoft.com/en-us/library/dd293608.aspx

#include <algorithm>
#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;
}
婴鹅 2024-07-17 22:04:48
for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++)
    sum += *it; 
for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++)
    sum += *it; 
囚我心虐我身 2024-07-17 22:04:48

第一个是类型正确的,并且在某种严格意义上是正确的。 (如果你考虑的是,大小永远不能小于零。) 不过,这个警告让我觉得是最容易被忽略的警告之一。

The first is type correct, and correct in some strict sense. (If you think about is, size can never be less than zero.) That warning strikes me as one of the good candidates for being ignored, though.

绝影如岚 2024-07-17 22:04:48

考虑是否需要迭代

标准标头为我们提供了执行此操作的工具:

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

算法库中的其他函数执行常见任务 - 确保您知道如果要保存哪些函数可用自己努力。

Consider whether you need to iterate at all

The <algorithm> standard header provides us with facilities for this:

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

Other functions in the algorithm library perform common tasks - make sure you know what's available if you want to save yourself effort.

书间行客 2024-07-17 22:04:48

晦涩但重要的细节:如果您如下说“for(auto it)”,您将获得对象的副本,而不是实际元素:

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]

要修改向量的元素,您需要将迭代器定义为引用:

for(auto &it : v)

Obscure but important detail: if you say "for(auto it)" as follows, you get a copy of the object, not the actual element:

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]

To modify the elements of the vector, you need to define the iterator as a reference:

for(auto &it : v)
凡间太子 2024-07-17 22:04:48

如果您的编译器支持它,您可以使用基于 for 的范围来访问向量元素:

vector<float> vertices{ 1.0, 2.0, 3.0 };

for(float vertex: vertices){
    std::cout << vertex << " ";
}

Prints: 1 2 3 。 请注意,您不能使用此技术来更改向量的元素。

If your compiler supports it, you could use a range based for to access the vector elements:

vector<float> vertices{ 1.0, 2.0, 3.0 };

for(float vertex: vertices){
    std::cout << vertex << " ";
}

Prints: 1 2 3 . Note, you can't use this technique for changing the elements of the vector.

山田美奈子 2024-07-17 22:04:48

添加此内容是因为我找不到任何答案中提到的内容:对于基于索引的迭代,我们可以使用 decltype(vec_name.size()) ,它将计算为 std::vector< T>::size_type

示例

for(decltype(v.size()) i{ 0 }; i < v.size(); i++) {
    /* std::cout << v[i]; ... */
}

Adding this as I couldn't find it mentioned in any answer: for index-based iteration, we can use decltype(vec_name.size()) which would evaluate to std::vector<T>::size_type

Example

for(decltype(v.size()) i{ 0 }; i < v.size(); i++) {
    /* std::cout << v[i]; ... */
}
童话 2024-07-17 22:04:48

两个代码段的工作原理相同。 但是,“unsigned int”路线是正确的。使用 unsigned int 类型将更好地与您使用它的实例中的向量配合使用。在向量上调用 size() 成员函数会返回一个无符号整数值,因此您需要比较该变量 。

另外,如果您对代码中“unsigned int”的外观仍然有点不安,请尝试“uint”,这基本上是“unsigned int”的缩写版本,它可以工作 您也不需要包含其他标头即可使用它。

The two code segments work the same. However, unsigned int" route is correct. Using unsigned int types will work better with the vector in the instance you used it. Calling the size() member function on a vector returns an unsigned integer value, so you want to be comparing the variable "i" to a value of its own type.

Also, if you are still a little uneasy about how "unsigned int" looks in your code, try "uint". This is basically a shortened version of "unsigned int" and it works exactly the same. You also don't need to include other headers to use it.

独享拥抱 2024-07-17 22:04:48
auto polygonsize = polygon.size(), i=polygonsize;
for (i=0; i < polygonsize; i++) {
    sum += polygon[i];
}

  • 使用 auto 来避免我们担心类型。
  • 它将任何函数调用(例如 size() 函数调用)移出循环,以避免不必要的重复函数调用。
  • 它使循环计数器可用。 纯粹主义者会想要在不知道 n 值的情况下使用第 n 个元素,并认为这很糟糕。
  • 它似乎有一个不必要的语句i=polygonsize在声明时初始化循环变量,但是如果有一个不错的代码优化器,这应该消失,并且仅仅是为了确保i 具有正确的类型。

我并不是说任何人都应该像我刚才那样编写任何代码。

我只是将它作为另一种替代方案提供,它可以避免担心类型,将函数调用从循环中取出,并使循环计数器可用于实际的事情,例如在更复杂的场景中调试信息。

auto polygonsize = polygon.size(), i=polygonsize;
for (i=0; i < polygonsize; i++) {
    sum += polygon[i];
}

This

  • uses auto to avoid us worrying about types.
  • It takes any function calls e.g. the size() function call out of the loop to avoid unnecessary repeated function calls.
  • It makes the loop counter available. Purists will want to work with the n'th element with no knowledge of the value of n, and see this as bad.
  • It appears to have an unecessary statement i=polygonsize initializing the loop variable when it's declared, but this should disappear if there is a half decent code optimizer, and is merely to ensure i has the correct type.

I am not saying anyone should code anything the way I just did.

I am merely offering it as another alternative which avoids worrying about types, takes function calls out of the loop, and makes the loop counter available for practical things like debugging information in more complex scenarios.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文