Qual é a maneira correta de iterar sobre um vetor em C ++?
Considere estes dois fragmentos de código, este funciona bem:
for (unsigned i=0; i < polygon.size(); i++) {
sum += polygon[i];
}
e este:
for (int i=0; i < polygon.size(); i++) {
sum += polygon[i];
}
que gera warning: comparison between signed and unsigned integer expressions
.
Eu sou novo no mundo do C ++, então a unsigned
variável me parece um pouco assustadora e sei que as unsigned
variáveis podem ser perigosas se não forem usadas corretamente, então - isso está correto?
.size()
não é do tipounsigned
akaunsigned int
. É do tipostd::size_t
.std::size_t
é um typedef definido por _implementation. Veja o padrão.std::size_t
pode ser equivalente àunsigned
sua implementação atual, mas isso não é relevante. Fingir que é pode resultar em código não portátil e comportamento indefinido.std::size_t
na prática. Você acha que já cobrimos tudo nesse fluxo de comentários durante seis anos?Respostas:
Para iterar para trás, veja esta resposta .
Iterar para frente é quase idêntico. Apenas altere o decremento de iteradores / swap por incremento. Você deve preferir iteradores. Algumas pessoas dizem para você usar
std::size_t
como o tipo de variável de índice. No entanto, isso não é portátil. Sempre use osize_type
typedef do contêiner (Embora você possa se livrar apenas de uma conversão no caso de iteração direta, ele pode realmente dar errado todo o caminho no caso de iteração reversa ao usarstd::size_t
, casostd::size_t
seja maior do que o typedef desize_type
) :Usando std :: vector
Usando iteradores
Importante é sempre usar o formulário de incremento de prefixo para iteradores cujas definições você não conhece. Isso garantirá que seu código seja executado o mais genérico possível.
Usando o intervalo C ++ 11
Usando índices
Usando matrizes
Usando iteradores
Usando o intervalo C ++ 11
Usando índices
Leia na resposta de iteração reversa para qual problema a
sizeof
abordagem pode render.fonte
for (auto p : polygon){sum += p;}
Quatro anos se passaram, o Google me deu essa resposta. Com o C ++ 11 padrão (também conhecido como C ++ 0x ), há realmente uma nova maneira agradável de fazer isso (ao preço de quebrar a compatibilidade com versões anteriores): a nova
auto
palavra-chave. Isso evita a necessidade de especificar explicitamente o tipo de iterador a ser usado (repetindo o tipo de vetor novamente), quando é óbvio (para o compilador), qual tipo usar. Comv
sendo o seuvector
, você pode fazer algo como isto:O C ++ 11 vai ainda mais longe e fornece uma sintaxe especial para iterar sobre coleções como vetores. Remove a necessidade de escrever coisas que são sempre as mesmas:
Para vê-lo em um programa de trabalho, crie um arquivo
auto.cpp
:Ao escrever isso, quando você o compila com o g ++ , normalmente é necessário configurá-lo para funcionar com o novo padrão, dando um sinalizador extra:
Agora você pode executar o exemplo:
Observe que as instruções de compilação e execução são específicas para o compilador gnu c ++ no Linux , o programa deve ser independente da plataforma (e compilador).
fonte
for (auto& val: vec)
std::vector<int> v = std::vector<int>();
, ou poderia simplesmente usá-lostd::vector<int> v;
?No caso específico do seu exemplo, eu usaria os algoritmos STL para fazer isso.
Para um caso mais geral, mas ainda bastante simples, eu diria:
fonte
Em relação à resposta de Johannes Schaub:
Isso pode funcionar com alguns compiladores, mas não com o gcc. O problema aqui é a questão se std :: vector :: iterator é um tipo, uma variável (membro) ou uma função (método). O seguinte erro ocorre com o gcc:
A solução está usando a palavra-chave 'typename', conforme informado:
fonte
T
é um argumento de modelo e, portanto, a expressãostd::vector<T*>::iterator
é um nome dependente. Para que um nome dependente seja analisado como um tipo, ele precisa ser anexado pelatypename
palavra - chave, conforme indica o diagnóstico.Uma chamada para
vector<T>::size()
retorna um valor do tipostd::vector<T>::size_type
, não int, int não assinado ou não.Também geralmente a iteração sobre um contêiner em C ++ é feita usando iteradores , como este.
Onde T é o tipo de dados que você armazena no vetor.
Ou utilizando os diferentes algoritmos de iteração (
std::transform
,std::copy
,std::fill
,std::for_each
et cetera).fonte
Use
size_t
:Citando a Wikipedia :
fonte
#include <cstddef>
ao AFAIK, em vez de<stddef.h>
, ou pior, a totalidade[c]stdlib
, e usarstd::size_t
em vez da versão não qualificada - e o mesmo para qualquer outra situação em que você tenha uma escolha entre<cheader>
e<header.h>
.Um pouco de história:
Para representar se um número é negativo ou não, use um bit de 'sinal'.
int
é um tipo de dados assinado, o que significa que pode conter valores positivos e negativos (cerca de -2 bilhões a 2 bilhões).Unsigned
só pode armazenar números positivos (e, como não desperdiça um pouco de metadados, pode armazenar mais: 0 a cerca de 4 bilhões).std::vector::size()
retorna umunsigned
, pois como um vetor pode ter comprimento negativo?O aviso está dizendo que o operando direito da sua declaração de desigualdade pode conter mais dados do que o esquerdo.
Essencialmente, se você tiver um vetor com mais de 2 bilhões de entradas e usar um número inteiro para indexar, terá problemas de estouro (o int retornará para 2 bilhões negativos).
fonte
Eu costumo usar BOOST_FOREACH:
Funciona em contêineres STL, matrizes, cadeias de estilo C, etc.
fonte
Para ser concluída, a sintaxe do C ++ 11 permite apenas uma outra versão para os iteradores ( ref ):
O que também é confortável para iteração reversa
fonte
Em C ++ 11
Eu usaria algoritmos gerais
for_each
para evitar procurar o tipo certo de iterador e expressão lambda para evitar funções / objetos extra nomeados.O pequeno exemplo "bonito" para o seu caso particular (assumindo que o polígono é um vetor de números inteiros):
testado em: http://ideone.com/i6Ethd
Não se esqueça de incluir: algoritmo e, é claro, vetor :)
A Microsoft também tem um bom exemplo disso:
fonte: http://msdn.microsoft.com/en-us/library/dd293608.aspx
fonte
fonte
O primeiro é o tipo correto, e correto em algum sentido estrito. (Se você pensa que é, o tamanho nunca pode ser menor que zero.) Esse aviso me parece um dos bons candidatos a ser ignorado.
fonte
i == INT_MAX
,i++
causa um comportamento indefinido. Neste ponto, tudo pode acontecer.Considere se você precisa iterar
O
<algorithm>
cabeçalho padrão fornece facilidades para isso:Outras funções na biblioteca de algoritmos realizam tarefas comuns - verifique se você sabe o que está disponível se quiser economizar seu esforço.
fonte
Detalhes obscuros mas importantes: se você disser "for (auto it)" da seguinte maneira, obterá uma cópia do objeto, não do elemento real:
Para modificar os elementos do vetor, você precisa definir o iterador como uma referência:
fonte
Se o seu compilador suportar, você pode usar um intervalo baseado em para acessar os elementos do vetor:
Impressões: 1 2 3. Observe que você não pode usar esta técnica para alterar os elementos do vetor.
fonte
Os dois segmentos de código funcionam da mesma maneira. No entanto, a rota unsigned int "está correta. O uso de tipos int não assinados funcionará melhor com o vetor na instância em que você o usou. Chamar a função de membro size () em um vetor retorna um valor inteiro não assinado, portanto, você deseja comparar a variável "i" para um valor de seu próprio tipo.
Além disso, se você ainda estiver um pouco desconfortável com a aparência de "unsigned int" em seu código, tente "uint". Esta é basicamente uma versão abreviada de "unsigned int" e funciona exatamente da mesma maneira. Você também não precisa incluir outros cabeçalhos para usá-lo.
fonte
Adicionando isso como não consegui encontrá-lo mencionado em nenhuma resposta: para iteração baseada em índice, podemos usar o
decltype(vec_name.size())
que seria avaliado comostd::vector<T>::size_type
Exemplo
fonte