Document No.: X3J16/93-0184 WG21/N0391 Date: 3 December 1993 Scope of declarations in for-init-statement Steve Rumsby Mathematics Institute University of Warwick Coventry CV4 7AL UK 1. Background Since the beginning C++ has allowed declarations in the ini- tialisation part of a for statement. The reason for this is the same as the reason for allowing declarations in the mid- dle of a block - to keep variable declarations as close as possible to the place where the variables are used, and preferably to initialise variables as they are declared. It is also more efficient in cases where default initialisation is expensive. Also the scope of such variables has always been until the end of the block containing the for state- ment. for(int i = 0; i < 42; i++) { //... } i = 42; // i still in scope here This behaviour causes problems when two or more loops in the same block use the same control variable: for(int i = 0; i < 42; i++) { //... } for(int i = 0; i < 42; i++) { // error: redefinition of i //... } But as I said, this is an old problem, and people are used to dealing with it. If this was the full extent of the problem, I believe we could live with it, although I would rather see it fixed. However recent working paper changes have made the problem much worse. X3J16/93-0184 - 1 - WG21/N0391 The Runtime Type Identification proposal adopted by the com- mittee in Portland introduced declarations into the selec- tion statements if and switch, and the iteration statements while and for. However, the scope of variables declared in these cases is limited to the statement or statements con- trolled by the selection or iteration statement. This means we can write for statements like this: for(int i = 0; int j = foo(); j = foo()) { //... } // i still visible here, but *not* j This is confusing. How do we justify it? We should expect many questions about this anomaly during the public review. We must have an answer to those questions. 2. Options So what can we do about this situation? I see the following possibilities, in no particular order: 1. Remove declarations in conditions. That is, reverse (part of) the Portland vote. I don't consider this acceptable - these declarations exist for a very good reason. In all the other constructs they produce no problems. In any case, they did not introduce the problem with the for statement, they just made it worse. 2. Remove declarations in the condition part of a for statement. This would complicate the language, and the teaching of it (``you can do this in a `while' loop but not a `for' loop''), and there are good reasons for wanting this construct in for loops. 3. Change the scope of variables declared in conditions to be the end of the enclosing block. This would remove the inconsistency, but in my opinion would make it con- sistently wrong. 4. As 3, but just for for statements. This would make for statements consistent, but again in my opinion consis- tently wrong. It would also make them inconsistent with while, if, and switch. 5. Remove the for-init-statement. That is, remove (or, more likely, deprecate) the facility that caused the problem in the first place. This would break more code that my proposed change, but in a way that is always noisy, and always easy to fix. But, as I said at the beginning, there are very good reasons for wanting X3J16/93-0184 - 2 - WG21/N0391 this. I don't believe this is an acceptable fix. 6. Change the scope of variables declared in the for-init- statement to be just the body of the loop. This is my preferred solution. 7. The status quo. We could just leave things the way they are. This leaves the language with a serious wart. It causes teachers to ``hide behind a desk'' (Jose, ext-1751) when they have to explain this behaviour. Is it a show stopper? I don't know - it could be for some. 3. The Proposal I propose that we change the scope of variables declared in for-init-statements to be the statement controlled by the for statement. This will match the scope of variables declared in the condition, and will be much more intuitive. The required working paper change is to change 6.5.3, para- graph 3 from: If the for-init-statement is a declaration, the scope of the names declared extends to the end of the block enclosing the for-statement. to: A name introduced by a declaration in a for-init- statement is in scope from its point of declara- tion until the end of the statements controlled by the for loop. 4. Objections ``This will break zillions of lines of code.'' Possibly true. I have no way of producing any numbers for this, but I suspect that the numbers will be lower than peo- ple think. In any case, such code will be broken in a way that is easily fixed. The only code affected, obviously, is code that uses the control variable of a for loop outside (after) the loop. In most of these cases the variable will then be undefined and the compiler will complain. Fixing these errors is easy. The harder case is when the affected code now picks up a variable with the same name declared in an enclosing block, or globally. This could produce a quiet change. How common is this likely to be? Some compilers (e.g. g++, but unfor- tunately not cfront) already produce a warning for for statements containing declarations which hide variables. People who currently heed this warning will not suffer from X3J16/93-0184 - 3 - WG21/N0391 such a quiet change. Perhaps this will serve as an incen- tive for more compilers to produce this warning. It is also possible for a compiler to detect and warn about just this particular case. 5. Conclusion I don't know of anybody who thinks the current situation is desirable, but the only reason people have for not fixing it is because it will break too much code. If we don't fix this now it most certainly will be too late to fix it after- wards, and C++ will be left with this wart for life. Many people express the opinion that 95% of C++ code is yet to be written. If that is true then it is not yet too late. Whatever we do, we must form an opinion, and a justification for that opinion, because we will certainly be asked about this during the public review. X3J16/93-0184 - 4 - WG21/N0391