È 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.