È possibile dichiarare due variabili di tipi diversi in un ciclo for?


240

È possibile dichiarare due variabili di diverso tipo nel corpo di inizializzazione di un ciclo for in C ++?

Per esempio:

for(int i=0,j=0 ...

definisce due numeri interi. Posso definire an inte a charnel corpo dell'inizializzazione? Come sarebbe fatto?


3
È possibile in g ++ - 4.4 ( -std=c++0x) sotto forma di for(auto i=0, j=0.0; ..., ma questa possibilità è stata rimossa in g ++ - 4.5 in coincidenza con i testi c ++ 0x.
rafak,

Risposte:


133

C ++ 17 : Sì! È necessario utilizzare una dichiarazione di associazione strutturata . La sintassi è supportata in gcc e clang da anni (dal momento che gcc-7 e clang-4.0) ( esempio live di clang ). Questo ci consente di decomprimere una tupla in questo modo:

for (auto [i, f, s] = std::tuple{1, 1.0, std::string{"ab"}}; i < N; ++i, f += 1.5) {
    // ...
}

Quanto sopra ti darà:

  • int i impostato 1
  • double f impostato 1.0
  • std::string s impostato "ab"

Assicurati di #include <tuple>questo tipo di dichiarazione.

Puoi specificare i tipi esatti all'interno tupledi digitandoli tutti come ho fatto con il std::string, se vuoi nominare un tipo. Per esempio:

auto [vec, i32] = std::tuple{std::vector<int>{3, 4, 5}, std::int32_t{12}}

Un'applicazione specifica di questo sta iterando su una mappa, ottenendo la chiave e il valore,

std::unordered_map<K, V> m = { /*...*/ };
for (auto& [key, value] : m) {
   // ...
}

Guarda un esempio dal vivo qui


C ++ 14 : puoi fare lo stesso di C ++ 11 (sotto) con l'aggiunta di tipo-based std::get. Quindi, invece che std::get<0>(t)nell'esempio seguente, puoi avere std::get<int>(t).


C ++ 11 : std::make_pairconsente di farlo, oltre che std::make_tupleper più di due oggetti.

for (auto p = std::make_pair(5, std::string("Hello World")); p.first < 10; ++p.first) {
    std::cout << p.second << std::endl;
}

std::make_pairrestituirà i due argomenti in a std::pair. È possibile accedere agli elementi con .firste .second.

Per più di due oggetti, dovrai usare a std::tuple

for (auto t = std::make_tuple(0, std::string("Hello world"), std::vector<int>{});
        std::get<0>(t) < 10;
        ++std::get<0>(t)) {
    std::cout << std::get<1>(t) << std::endl; // cout Hello world
    std::get<2>(t).push_back(std::get<0>(t)); // add counter value to the vector
}

std::make_tupleè un modello variadico che costruirà una tupla di qualsiasi numero di argomenti (con alcune limitazioni tecniche ovviamente). È possibile accedere agli elementi tramite indice constd::get<INDEX>(tuple_object)

All'interno dei corpi del ciclo for è possibile facilmente alias degli oggetti, sebbene sia ancora necessario utilizzare .firsto std::getper la condizione del ciclo for e l'espressione di aggiornamento

for (auto t = std::make_tuple(0, std::string("Hello world"), std::vector<int>{});
        std::get<0>(t) < 10;
        ++std::get<0>(t)) {
    auto& i = std::get<0>(t);
    auto& s = std::get<1>(t);
    auto& v = std::get<2>(t);
    std::cout << s << std::endl; // cout Hello world
    v.push_back(i); // add counter value to the vector
}

C ++ 98 e C ++ 03 È possibile assegnare un nome esplicito ai tipi di a std::pair. Non esiste un modo standard per generalizzare questo in più di due tipi:

for (std::pair<int, std::string> p(5, "Hello World"); p.first < 10; ++p.first) {
    std::cout << p.second << std::endl;
}

5
Se stai eseguendo C ++ 17, puoi persino rilasciare make_e scrivere std::pair(1, 1.0).
Marc Glisse,

La pelosa tupla / coppia in stile C ++ 14 - tutto bene (probabilmente, votato), ma sembra bizzarro :)
mlvljr

3
In breve: sì, è possibile, ma non sarà bello.
Qualche programmatore, amico, il

Sì, non carino, ma è stupido! Assolutamente apprezzato il tuple-ish. :) Ma in realtà è una qualità sintattica molto poco intuitiva per i loop in C ++ e mi ha fatto venire il mal di testa per più di mezz'ora per rendermi finalmente conto di ciò che doveva essere Googled ...
aderchox

@aderchox se riesci a chiarire il tuo malinteso posso aggiornare la risposta
Ryan Haining,

276

No, ma tecnicamente c'è una soluzione (non che lo userei davvero a meno che non sia costretto):

for(struct { int a; char b; } s = { 0, 'a' } ; s.a < 5 ; ++s.a) 
{
    std::cout << s.a << " " << s.b << std::endl;
}

3
Questo non viene compilato su VS 2008, ma su Comeau online ;-)
JRL

7
@JRL: Oh, nemmeno VS2005. Un'altra caratteristica di non conformità in VC ++ credo.
Georg Fritzsche,

3
Ho fatto l'equivalente in Perl. Non ho provato a intrufolarmi in qualcosa del genere attraverso una revisione del codice in C ++, però.
Giovanni,

21
con c ++ 11 I puoi rendere più breve questo esempio usando i valori predefinitistruct { int a=0; char b='a'; } s;
Ryan Haining,

1
Questa risposta soddisfa i requisiti della risposta, ma da un POV di leggibilità preferisco @MK. la risposta. La soluzione di MK si rivolge persino allo scoping aggiungendo le parentesi graffe.
Trevor Boyd Smith,

221

Non possibile, ma puoi fare:

float f;
int i;
for (i = 0,f = 0.0; i < 5; i++)
{
  //...
}

Oppure, limitare esplicitamente l'ambito fe l' iutilizzo di parentesi aggiuntive:

{
    float f; 
    int i;
    for (i = 0,f = 0.0; i < 5; i++)
    {
       //...
    }
}

So che questa è una domanda molto vecchia, ma puoi spiegare perché alcuni lo farebbero con le parentesi extra attorno, come nel tuo secondo esempio?
ford

13
@fizzisist per limitare esplicitamente l'ambito di f e i alle sole parti del codice in cui vengono utilizzate.
MK.

1
@MK. Grazie, questo è quello che sospettavo. Ho modificato la tua risposta per spiegarlo.
ford,

Solo una domanda: perché in questo modo? : O
rohan-patel,

Perché funziona come 'int a = 0, b = 4', presumo. Detto questo, l'ambito f e i sarà probabilmente utile solo per impedire il riutilizzo di quei nomi (che è una buona ragione), ma il codice generato sarà in genere lo stesso su un compilatore moderno (in questo caso).
Asu,

14

Non è possibile dichiarare più tipi nell'inizializzazione, ma è possibile assegnare a più tipi EG

{
   int i;
   char x;
   for(i = 0, x = 'p'; ...){
      ...
   }
}

Dichiarali nel loro ambito.


3

Penso che l'approccio migliore sia la risposta di xian .

ma...


# Nidificato per loop

Questo approccio è sporco, ma può risolvere in tutte le versioni.

quindi, lo uso spesso nelle funzioni macro.

for(int _int=0, /* make local variable */ \
    loopOnce=true; loopOnce==true; loopOnce=false)

    for(char _char=0; _char<3; _char++)
    {
        // do anything with
        // _int, _char
    }

Ulteriori 1.

Può anche essere usato per declare local variablese initialize global variables.

float globalFloat;

for(int localInt=0, /* decalre local variable */ \
    _=1;_;_=0)

    for(globalFloat=2.f; localInt<3; localInt++) /* initialize global variable */
    {
        // do.
    }

Ulteriori 2.

Buon esempio: con la funzione macro.

(Se l' approccio migliore non può essere utilizzato perché è una macro for-loop)

#define for_two_decl(_decl_1, _decl_2, cond, incr) \
for(_decl_1, _=1;_;_=0)\
    for(_decl_2; (cond); (incr))


    for_two_decl(int i=0, char c=0, i<3, i++)
    {
        // your body with
        // i, c
    }

# Trucco if-statement

if (A* a=nullptr);
else
    for(...) // a is visible

Se vuoi inizializzare 0o nullptr, puoi usare questo trucco.

ma non lo consiglio a causa della difficile lettura.

e sembra un bug.


Non smette mai di stupirmi di quanto diverse persone pensano dagli altri. Non avrei mai pensato a tali stranezze. Idee interessanti.
Dr. Person Person II

1

Vedere " Esiste un modo per definire variabili di due tipi in for loop? " Per un altro modo che coinvolge l'annidamento multiplo per loop. Il vantaggio dell'altro modo rispetto al "trucco della struttura" di Georg è che (1) ti permette di avere una combinazione di variabili locali statiche e non statiche e (2) ti permette di avere variabili non copiabili. Il rovescio della medaglia è che è molto meno leggibile e potrebbe essere meno efficiente.


-2

Definisci una macro:

#define FOR( typeX,x,valueX,  typeY,y,valueY,  condition, increments) typeX x; typeY y; for(x=valueX,y=valueY;condition;increments)

FOR(int,i,0,  int,f,0.0,  i < 5, i++)
{
  //...
}

Ricorda solo che i tuoi ambiti variabili non saranno inclusi nel ciclo for in questo modo.


Si potrebbe facilmente superare tale limitazione avvolgendo il codice nella macro in un ambito separato usando {e }.
Nathan Osman,

4
No, non poteva. La sua macro non avvolge il corpo del loop. Potrebbe aggiungere una parentesi aperta aggiuntiva, ma ciò richiederebbe una parentesi di chiusura "extra" quando si utilizza la macro.
Giovanni,

3
È un'idea interessante, ma prima di prendere in considerazione utilizzerei prima una qualsiasi delle altre risposte.
gregn3,

-2

Inoltre puoi usare come sotto in C ++.

int j=3;
int i=2;
for (; i<n && j<n ; j=j+2, i=i+2){
  // your code
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.