2016-01-24 17 views
65

consideri:Perché questo ciclo 'for' non è valido?

int ia[3][4]; 
for (auto row : ia) 
     for (auto col : row) 

Il primo for itera ia, i cui elementi sono matrici di dimensione 4. Poiché row non è un riferimento, quando il compilatore inizializza row Si converte ogni elemento dell'array (come qualsiasi altro oggetto del tipo array ) a un puntatore al primo elemento dell'array. Di conseguenza, in questo ciclo il tipo di row è int*.

io non sono davvero sicuro che ho capito come funziona questa auto, ma se posso supporre che dà automaticamente un tipo a una riga sulla base di ia membri di array di tipo, ma non capisco il motivo per cui questo tipo di for , dove la riga non è un riferimento, non è valida. Perché sta per succedere? "puntatore al primo elemento dell'array", a causa di cosa?

+6

Ho trovato questo in C++ Primer 5th Edition di Lippman, pagina 182. –

risposta

71

Il problema è che è un rowint * e non un int[4] come ci si aspetta, perché gli array decadimento di puntatori e non v'è alcun modo automatico per sapere quanti elementi puntatore viene.

per aggirare questo problema std::array è stato aggiunto in cui tutto funziona come previsto:

#include <array> 

int main() { 
    std::array<std::array<int, 4>, 3> ia; 
    for (auto &row : ia){ 
     for (auto &col : row){ 
      col = 0; 
     } 
    } 
} 

Annotare il & prima row e col che indicano che si desidera un riferimento e non una copia delle righe e delle colonne, in caso contrario, l'impostazione di col su 0 non avrebbe alcun effetto su ia.

+7

+1 per rinforzi i loop, e l'utilizzo di C++ 11 funzioni correttamente. In un ambiente solo C++, questo è il modo migliore per gestirlo. – WeRelic

+49

Si noti che l'equivalente di 'int [3] [4]' è _not_ 'std :: array , 4>', ma 'std :: array , 3>'. Questo è un errore comune e vale la pena indicarlo, doppiamente perché lo hai fatto anche tu. ; -] – ildjarn

+0

@Deduplicator quali copie temporanee? – Quentin

38

Per evitare il decadimento del int[] per int* è possibile utilizzare &&

int main() { 
    int ia[3][4]; 
    for (auto && row : ia) 
     for (auto && col : row) 
      ; 
} 
+8

Oppure sarebbe sufficiente un semplice riferimento. –

+1

In realtà, dovresti usare i riferimenti rvalue ('&&') solo nei costruttori di movimento, spostare gli operatori di assegnazione o quando i riferimenti semplici non funzionano e sai esattamente cosa stai facendo. –

+6

Ho usato '&&' qui specificamente perché è quello che viene proposto in [N3853] (http://open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3853.htm) per supportare la ancora più ridotta '(elem: collection)' sintassi – Thomas