ti-enxame.com

Loops FOR aprimorados em C ++

Estou mudando de Java para C++ e fiquei pensando se o C++ contém os loops aprimorados que usei em Java, por exemplo:

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

Esse mesmo "atalho" é possível em C++?

54
jozefg

No C++ 11, se o seu compilador suportar, sim, é. Chama-se range-based for.

std::vector<int> v;

// fill vector

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

Ele funciona para matrizes de estilo C e qualquer tipo que tenha as funções begin() e end() que retornam iteradores. Exemplo:

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
    }
}
61
jrok

C++ 11 sim. Eles são chamados de fors baseados em intervalo. Lembre-se de que você deve qualificar o tipo como uma referência ou uma referência a const.

A solução alternativa para C++ 03 é BOOST_FOR_EACH ou boost :: bind em combinação com std :: for_each . Coisas mais chiques são possíveis com o Boost.Lambda. Caso esteja com vontade de frustrar você ou seus colegas de trabalho, recomendo os fichários reprovados std::bind1st e std::bind2nd.

Aqui está um exemplo de código:

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

Não existe essa possibilidade no C++ 03. No entanto, o novo padrão (C++ 11) o possui. Veja o exemplo (retirado de Wikipedia ):

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

Considere também usar std::vector<int> em vez de uma matriz comum. Essa é a analogia em C++ para tipos de dados em C, o que facilita a vida.

13
Beginner

Sim e não.

1. Matriz local: Não, mas você pode encontrar facilmente o tamanho

Se você tiver uma matriz local (int numbers[4] = {1, 2, 3, 4];), Poderá executar size = sizeof(numbers) / sizeof(int).

2. Ponteiro para o array: de modo algum, você precisa passar o tamanho separadamente

Se você tiver um ponteiro para uma matriz (int* numbers = new int[4];), Não poderá descobrir o tamanho, a menos que você mesmo o controle. (ou se for nulo terminado no caso de uma cadeia c, mas você precisará iterar através dela, que é o tempo de execução linear ...)

Observe que eu não acredito que o ponteiro para o array seja a terminologia apropriada; na verdade, você só tem um ponteiro para o primeiro elemento do array, mas o espaço para vários valores foi alocado. Não tenho certeza do que isso é chamado. Talvez apenas um ponteiro?

. Contêineres STL: Sim, e você pode fazer alguns para mágica de loop usando iteradores, ou apenas usar índices obtendo o tamanho

Se você tiver um vetor (std::vector<int> v(3, 0);), poderá iterá-lo das seguintes maneiras:

C++ 11:

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

Ou aparentemente (também C++ 11, obrigado jrok):

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

C++ (pré-11):

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

Ou usando índices:

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

Além disso, você pode usar ponteiros de função ou functores com contêineres STL usando for_each (#include <algorithm>) Do algoritmo std da seguinte maneira:

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

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

No padrão antigo, C++ 03 (que é de 2003), o idioma não tem suporte interno para esse tipo de loop for. Existem alguns artifícios que você pode usar com o Boost, mas não vale a pena incluir uma biblioteca totalmente nova para esse pequeno recurso de conveniência.

No novo padrão, C++ 11 (lançado no último verão), isso é possível; a sintaxe é assim:

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

Observe que estou usando MyType& x, não MyType x. Em Java tudo é uma referência. Em C++, as referências devem ser explícitas e você as declara usando &. Se você não usar referências, o loop for copiará cada elemento da matriz em x (o que pode ser caro).

No entanto, o C++ 11 ainda não é totalmente suportado pela maioria dos compiladores. Acho que o Visual C++ da Microsoft suporta esse recurso, mas não tenho certeza.

6
Paul Manta

Outros já mencionaram que esse estilo de loop foi adicionado no C++ 11. No entanto, o C++ 11 é ainda melhor:

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

Dessa forma, se você alterar posteriormente o tipo de elemento numbers de int para long, ou mesmo para alguma classe bigint que você escreveu, não será necessário ' Não é necessário alterar isso para o loop em tudo.

6
celtschk

Acho essa macro simples muito útil. A grande maioria dos meus loops for envolve a iteração sobre um contêiner STL:

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

Um exemplo:

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

Há duas coisas que você deve estar ciente disso: primeiro, é um iterador e, portanto, você deve desreferê-lo. E segundo, o segundo parâmetro para For será avaliado várias vezes. Eu brinquei com outras abordagens, mas continuo retornando à simplicidade disso.

3
Aaron McDaid