Улучшенные циклы FOR в C ++ - PullRequest
       0

Улучшенные циклы FOR в C ++

54 голосов
/ 05 декабря 2011

Я переключаюсь с Java на C ++, и мне было интересно, есть ли в C ++ расширенные циклы for, которые я использовал в java, например:

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

Возможен ли такой же "ярлык" в C ++?

Ответы [ 7 ]

73 голосов
/ 05 декабря 2011

C ++ 11 делает.Они называются форс-мажорами.Помните, что вы должны квалифицировать тип как ссылку или ссылку на const.

Обходной путь для C ++ 03 - BOOST_FOR_EACH или boost :: bind в комбинациис std :: for_each .С Boost.Lambda возможны более причудливые вещи.Если вы хотите расстроить себя или своих коллег, я рекомендую устаревшие папки std::bind1st и std::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;
}
61 голосов
/ 05 декабря 2011

В C ++ 11, если ваш компилятор поддерживает это, да, это так. Это называется на основе диапазона для.

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
    }
}
13 голосов
/ 05 декабря 2011

В C ++ 03 такой возможности нет. Однако новый стандарт (C ++ 11) имеет это. Смотрите пример (взято из Wikipedia ):

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

Также рассмотрите возможность использования std::vector<int> вместо обычного массива. Это аналогия C ++ для типов данных C, которая облегчает жизнь.

12 голосов
/ 05 декабря 2011

Да и нет.

1. Локальный массив: нет, но вы легко можете найти размер

Если у вас есть локальный массив (int numbers[4] = {1, 2, 3, 4];), тогда вы можете сделать size = sizeof(numbers) / sizeof(int).

2. Указатель на массив: совсем нет, вы должны передать размер отдельно

Если у вас есть указатель на массив (int* numbers = new int[4];), вы не сможете определить размер, если не будете следить за ним самостоятельно. (или если в случае строки c она завершается нулем, но вам придется перебирать ее, что является линейным временем выполнения ...)

Обратите внимание, что я не верю, что указатель на массив является правильной терминологией, на самом деле у вас просто есть указатель на первый элемент массива, но выделено место для нескольких значений. Не уверен, как это называется. Может быть, просто указатель?

3. Контейнеры STL: Да, и вы можете сделать некоторые для магии цикла, используя итераторы, или просто использовать индексы, получив размер

Если у вас есть вектор (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 ++ (до 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]);
}

Кроме того, вы можете использовать указатели функций или функторы с контейнерами STL, используя алгоритм std for_each (#include <algorithm>), например:

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

{
    std::for_each(myvector.begin(), myvector.end(), foo);
}
6 голосов
/ 05 декабря 2011

Другие уже упоминали, что этот стиль цикла был добавлен в C ++ 11. Однако C ++ 11 еще лучше:

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

Таким образом, если позже вы измените тип элемента numbers с int на long или даже на какой-то класс bigint, который вы написали сами, вам не нужно менять это для цикла вообще.

6 голосов
/ 05 декабря 2011

В старом стандарте, C ++ 03 (выпущенном в 2003 году), в языке нет встроенной поддержки для такого цикла for. Есть некоторые артефакты, которые вы можете использовать с Boost, но, тем не менее, не стоит включать совершенно новую библиотеку для этой небольшой удобной функции.

В новом стандарте C ++ 11 (который был выпущен только прошлым летом) это возможно; синтаксис выглядит так:

MyType array[] = { ... }
for (MyType& x : array) {
    ...
}

Обратите внимание, что я использую MyType& x, а не MyType x. В Java все является ссылкой. В C ++ ссылки должны быть явными, и вы объявляете их, используя &. Если вы не используете ссылки, цикл for скопирует каждый элемент массива в x (что может быть дорого).

Однако C ++ 11 еще не полностью поддерживается большинством компиляторов. Я думаю, что Microsoft Visual C ++ поддерживает эту функцию, но я не уверен.

3 голосов
/ 05 декабря 2011

Я считаю этот простой макрос очень полезным. Подавляющее большинство моих циклов 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 будет оцениваться много раз. Я играл с другими подходами, но я продолжаю возвращаться к простоте этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...