C++中的增强型FOR循环

66

我正在从Java转向C++,想知道C++中是否包含我在Java中使用的增强for循环,例如:

int[] numbers = {1,2,3,4,5,6,7,8,9,10};
for (int item : numbers) {
  System.out.println("Count is: " + item);
}

在C++中是否存在类似的“快捷方式”?


42
对于刚切换到C++的人来说,可能不了解C++版本控制的工作原理。为什么不给出一个实际的答案,解释与问题相关的差异呢? - millimoose
5
@PaulManta 没有。我同意完全解释所有内容可能更好,但在 OP 说“我不知道 C++XX 是什么”之后也可以这样做。我只是想给出一个更短的答案..... - Roman Byshko
6
@Paul:仅仅因为别人可能不知道答案就提出问题并没有什么自以为是的意思。这个问题很合理。 - ildjarn
这是 C# 中 foreach 的等价物吗? - jameskind
这个问题有很多很棒的例子。非常不错。 - pyCthon
7个回答

77

C++11有一种叫做range-based for的方法。请记得要将类型标识为引用或者const引用。

对于C++03,可以使用BOOST_FOR_EACHboost::bindstd::for_each结合使用来解决问题。使用Boost.Lambda还可以实现更多高级功能。如果你想让自己或同事感到沮丧,建议使用已被弃用的绑定器std::bind1ststd::bind2nd

以下是一些示例代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <boost/lambda/lambda.hpp>
#include <functional>    

int main()
{
  int i = 0;
  std::vector<int> v;
  std::generate_n(std::back_inserter(v), 10, [&]() {return i++;});

  // range-based for
  // keep it simple
  for(auto a : v)
    std::cout << a << " ";
  std::cout << std::endl;

  // lambda
  // i don't like loops
  std::for_each(v.begin(), v.end(), [](int x) { 
      std::cout << x << " ";
    });
  std::cout << std::endl;

  // hardcore
  // i know my lib
  std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << std::endl;


  // boost lambda
  // this is what google came up with
  // using for the placeholder, otherwise this looks weird
  using namespace boost::lambda;
  std::for_each(v.begin(), v.end(), std::cout << _1 << " ");
  std::cout << std::endl;

  // fold
  // i want to be a haskell programmer
  std::accumulate(v.begin(), v.end(), std::ref(std::cout), 
                  [](std::ostream& o, int i) -> std::ostream& { return o << i << " "; });

  return 0;
}

5
讽刺的评论很搞笑,点赞。 - Nicholas Palko
+1. 我没想到std::accumulate可以这样使用。虽然我不会在我的代码中使用它,但我喜欢它在这里的用法。 - Nawaz

71
在C++11中,如果你的编译器支持,是可以的。它被称为范围-based for。
std::vector<int> v;

// fill vector

for (const int& i : v) { std::cout << i << "\n"; }

它适用于C样式数组以及任何具有函数begin()end()返回迭代器的类型。例如:

class test {
    int* array;
    size_t size;
public:
    test(size_t n) : array(new int[n]), size(n)
    {
        for (int i = 0; i < n; i++) { array[i] = i; }
    }
    ~test() { delete [] array; }
    int* begin() { return array; }
    int* end() { return array + size; }
};

int main()
{
    test T(10);
    for (auto& i : T) {
        std::cout << i;   // prints 0123456789
    }
}

我不明白这个答案怎么比我的迭代序列的完整手册更好。;) - pmr
1
你说得没错,我认错并且被你说服了。 - pmr
1
@Merlin 我在这里看不到任何假设。只有事实。这些事实清楚地表明,在不同版本的 C++ 中,有许多打印某些东西的方法,其中一些更方便,需要程序员知道每种方法。不包括使用 accumulate 的那种方法。 - pmr
它的行为几乎与Python for循环相同。 - ncmathsadist
使用C11,当使用“int”时会收到警告:for (int i = 0; i < n; i++){ ... }, 警告:比较不同符号的整数:“int”和“size_t”(又名“unsigned long”),因为您声明了“size_t size;” for (size_t i = 0; i < n; i++) { ... } - NaturalDemon
显示剩余2条评论

15

在C++03中不存在这样的可能性。然而新标准(C++11)具有此功能。请参见示例(摘自维基百科):

int my_array[5] = {1, 2, 3, 4, 5};
for (int &x : my_array) {
    x *= 2;
}

考虑使用std::vector<int>而不是普通数组。这是C++对C数据类型的类比,使生活更加轻松。


它怎么知道数组的长度是多少? - Lukasz Madon
@lukas 编译器会知道。你也可以自己计算 int arr_len = sizeof(my_array) / sizeof(my_array[0]); - Roman Byshko
那么换句话说,我只能将它们用于“堆栈”数组吗?没有指向数组的指针? - Lukasz Madon
@lukas 是的。但是看一下这个 https://dev59.com/RGsy5IYBdhLWcg3wsQJj#8378615 还有其他情况也可以用它。 - Roman Byshko

12

是和否。

1. 本地数组:不行,但你可以轻松找到大小

如果你有一个本地数组(int numbers[4] = {1, 2, 3, 4};),那么你可以这样做size = sizeof(numbers) / sizeof(int)

2. 数组指针: 不行,你必须单独传递大小

如果你有一个数组指针(int* numbers = new int[4];),除非你自己跟踪大小,否则无法确定大小。(或者对于c字符串的情况下是以null结尾的,但然后你必须迭代它,这是线性运行时间...)

请注意,我不认为数组指针是正确的术语,实际上你只是有指向数组第一个元素的指针,但已经分配了多个值的空间。不确定这被称为什么。也许只是一个指针?

3. STL容器:可以,你可以使用迭代器进行一些for循环魔法,或者通过获取大小使用索引

如果你有一个向量(std::vector<int> v(3, 0);),那么可以按以下方式迭代:

C++11:

auto it = v.begin();
for (auto it = v.begin(); it != v.end(); it++)
{
    UseElement(*it);
}

或者显然(也适用于C++11,感谢jrok):

for (const int& i : v) { UseElement(i); }

C++ (pre-11):

->

C++(11年前版本):

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

或者使用索引:

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

此外,您可以使用函数指针或functor与STL容器一起使用std算法的for_each(#include <algorithm>),如下所示:

void foo(int i)
{
    std::cout << i;
}

{
    std::for_each(myvector.begin(), myvector.end(), foo);
}

6
在旧标准C++03(2003年发布)中,该语言没有内置支持这种类型的for循环。您可以使用Boost进行一些技巧处理,但在我看来,为此小小的便利功能包含整个新库是不值得的。
在新标准C++11(去年夏天刚发布)中,这是可能的;语法如下:
MyType array[] = { ... }
for (MyType& x : array) {
    ...
}

请注意,我使用的是而不是。在Java中,一切皆为引用。在C ++中,引用必须明确,并且您可以使用&进行声明。如果您不使用引用,则for循环将复制数组的每个元素到中(这可能是昂贵的操作)。
然而,大多数编译器还不完全支持C++11。我认为微软的Visual C++支持此功能,但我不确定。

我猜你的意思是 MyType array[] 而不是 int MyType[] - celtschk

6

其他人已经提到这种循环风格是在C++11中添加的。然而,C++11更好:

for (auto const& item: numbers)
{
  std::cout << "Count is: " << item << '\n';
}

这样,如果以后您将numbers元素类型从int更改为long,甚至是您自己编写的bigint类,则不需要对那个循环进行任何更改。


3

我发现这个简单的宏非常有用。我的绝大多数for循环都涉及迭代STL容器:

#define For(it, container) for( typeof((container).begin()) it = (container).begin(); it != (container).end(); ++it)

一个例子:
vector<int> vector_of_ints;
... // initialize it somehow
For(integer, vector_of_ints) {
    cout << *integer << endl;
}

有两件事情需要注意:首先,它是一个迭代器,因此您必须对其进行解引用。其次,For的第二个参数将被评估多次。我尝试过其他方法,但最终还是回到这种简单的方法。


1
请注意,typeof运算符是非标准的C ++。 - Firas Assaad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接