Расширенные циклы FOR в С++
Я переключаюсь с Java на С++, и мне было интересно, содержит ли С++ расширенные циклы, которые я использовал в java, например:
int[] numbers = {1,2,3,4,5,6,7,8,9,10};
for (int item : numbers) {
System.out.println("Count is: " + item);
}
Является ли тот же самый "ярлык" возможным в С++?
Ответы
Ответ 1
В С++ 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
}
}
Ответ 2
С++ 11. Они называются базами fors. Помните, что вы должны квалифицировать тип как ссылку или ссылку на const.
Обходной путь для С++ 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;
}
Ответ 3
В С++ 03 такой возможности нет. Однако новый стандарт (С++ 11) действительно имеет его. См. Пример (взято из Wikipedia):
int my_array[5] = {1, 2, 3, 4, 5};
for (int &x : my_array) {
x *= 2;
}
Рассмотрим также использование std::vector<int>
вместо обычного массива. Это аналог С++ для типов данных C, что облегчает жизнь.
Ответ 4
Да и нет.
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);
), вы можете выполнить итерацию через него следующими способами:
С++ 11:
auto it = v.begin();
for (auto it = v.begin(); it != v.end(); it++)
{
UseElement(*it);
}
Или, по-видимому (также С++ 11, спасибо jrok):
for (const int& i : v) { UseElement(i); }
С++ (pre-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);
}
Ответ 5
В старом стандарте С++ 03 (начиная с 2003 г.) язык не имеет встроенной поддержки такого типа for-loop. Есть несколько уловок, которые вы можете использовать с Boost, но не стоит включать в себя всю новую библиотеку для этой небольшой функции удобства.
В новом стандарте С++ 11 (который был выпущен только прошлым летом), это возможно; синтаксис выглядит следующим образом:
MyType array[] = { ... }
for (MyType& x : array) {
...
}
Обратите внимание, что я использую MyType& x
, а не MyType x
. В Java все является ссылкой. В С++ ссылки должны быть явными, и вы объявляете их с помощью &
. Если вы не используете ссылки, for-loop будет копировать каждый элемент массива в x
(что может быть дорогостоящим).
Однако С++ 11 еще не полностью поддерживается большинством компиляторов. Я думаю, что Microsoft Visual С++ поддерживает эту функцию, но я не уверен.
Ответ 6
Другие уже упомянули, что этот стиль цикла был добавлен в С++ 11. Однако С++ 11 еще лучше:
for (auto const& item: numbers)
{
std::cout << "Count is: " << item << '\n';
}
Таким образом, если вы позже измените тип элемента numbers
с int
на long
или даже на некоторый класс bigint
, который вы написали сами, вам не нужно изменять это для цикла вообще.
Ответ 7
Я нахожу этот простой макрос очень полезным. Подавляющее большинство моих циклов 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
будет оцениваться много раз. Я играл с другими подходами, но я продолжаю возвращаться к простоте этого.