Qual è l’uso ansible di “#define for if (false) {} else for”?

In un’altra domanda, ho appena avvistato questa piccola perla della saggezza C :

#define for if (false) {} else for 

che ha indotto MSVC a sputare avvisi di “espressione costante” per una dichiarazione abbastanza valida:

 for (int i = 0; i <= 10; i++) {...} 

Capisco perché MSVC si lamenta perché si espande a:

 if (false) {} else for (int i = 0; i <= 10; i++) {...} 

Semplicemente non capisco perché gli sviluppatori dovrebbero usare quel piccolo frammento. Qualcuno ha un’idea?

È per correggere un bug nelle vecchie versioni di Visual C ++ (v6.0 e precedenti). In passato, Visual C ++ aveva infranto le regole dell’ambito sulle variabili dichiarate all’interno for istruzioni:

 // This compiles in old versions of Visual C++, but it is in fact INVALID C++ for(int i = 0; ...) { ... } for(i = 0; ...) { } 

In altre parole, Visual C ++ fornisce un ambito come se fosse dichiarato al di fuori del ciclo e consente di continuare a utilizzarlo una volta completato il ciclo. Questo ha portato a codice come il frammento di cui sopra. In più compilatori conformi agli standard, i non è più incluso nella definizione del secondo ciclo for , quindi il compilatore genera un errore in quanto non è definito.

Per risolvere questo problema, alcune persone hanno utilizzato questa macro (o macro equivalenti molto simili):

 #define for if(0) {} else for 

Questo cambia il ciclo for in questo:

 if(0) { } else for(int i = 0; ...) { ... } 

Questo mette il ciclo for in un livello extra di scope, in modo che tutte le variabili dichiarate nel ciclo for siano fuori dallo scope in seguito, indipendentemente dall’errore di Visual C ++. Ciò garantisce che lo stesso codice compili correttamente in modo coerente in Visual C ++ e compilatori conformi agli standard e che il codice non corretto non venga compilato in modo corretto in modo coerente.

Si noti inoltre che se la macro fosse invece definita così:

 // DO NOT USE #define for if(1) for 

Quindi, anche se ciò avrebbe lo stesso effetto per un semplice codice, improvvisamente causerebbe la compilazione errata del seguente codice:

 if(foo) for(...) { ... } else doSomething(); 

Perché se espandi la macro, ottieni questo:

 if(foo) if(1) for(...) { ... } else doSomething(); 

E il else ora combacia con il male if ! Quindi, l’uso intelligente dell’uso di if(0) {} else invece di if(1) evita questo problema.

Come nota finale, #define for if(0) {} else for non causa una ricorsione infinita, perché il preprocessore non sostituirà ricorsivamente la macro che stai attualmente definendo. In questo caso, eseguirà solo una sostituzione.

Secondo una rapida ricerca è un bug in MSVC che viene superato.

A quanto ho capito,

 for (int i = 0 ...) {.....} 
 // più tardi allo stesso livello di ambito nella stessa funzione
 for (int i = 0 ...) {...}

causerà una ridefinizione dell’errore “i”.

Se l’istruzione for è inclusa in un’istruzione if, il compilatore funziona come dovrebbe, in modo che non vi sia alcun errore di ridefinizione (apparentemente interpreta i livelli di ambito di “if” ma non “for”)

Poiché il compilatore msvc gestisce in modo errato l’ambito delle variabili dichiarate nell’istruzione for per impostazione predefinita. Per evitare questo comportamento, è stato necessario distriggersre le estensioni microsoft che hanno reso le intestazioni ms non compilate.

Io uso (sì, io uso ancora vs6) uno diverso che non causa l’avviso in VS6, anche se il compilatore Intel lo vede ancora.

 #define for switch(0) case 0: default: for 

Non riesco a ricordare da dove l’ho preso, ma dubito che l’ho inventato 😉

So che le altre risposte dicono già la maggior parte di questo, ma il pop-up dice di essere sicuro di rispondere alla domanda.

L’effetto era già stato descritto.

La ragione per averlo è di portare il codice C ++ in MSVC. O è anche molto utile se vuoi che la tua piattaforma di codice C ++ sia indipendente. Ad esempio, lo hai sviluppato su Linux / MacOSX e ora vuoi compilarlo in MSVC.

Ed è anche molto utile per C ++ stesso. Per esempio:

 for(std::set::iterator i = myset.begin(); i != myset.end(); ++i) { // ... } for(int i = 0; i < N; ++i) { // ... } 

Ho visto il codice MSVC che ha funzionato su questo facendo uno dei due:

 for(std::set::iterator i1 = myset.begin(); i1 != myset.end(); ++i1) { // ... } for(int i2 = 0; i2 < N; ++i2) { // ... } 

O:

 {for(std::set::iterator i = myset.begin(); i != myset.end(); ++i) { // ... }} {for(int i = 0; i < N; ++i) { // ... }} 

In entrambi i casi (imo) non è bello. E questo #define è un piccolo trucco per rendere MSVC più standard.