6

While browsing some C++ code, I came across the following lines :

for (int i = 0; i < count; i++) {
        if (&array[i].GetData() == el)
            break;
}
if (i < count) {
   // .. Do something
}

I am surpised to see that the loop-counter variable i is accessible outside the loop!

Just to ensure that the i outside the loop was same as the one inside the loop, I changed the loop variable name to i1.

for (int i1 = 0; i1 < count; i1++) {
        if (&array[i1].GetData() == el)
            break;
}
if (i < count) {    // COMPILATION ERROR: Identifier i is undefined
   // .. Do something
}

This resulted in a compilation error for the line if(i < count) :

identifier 'i' is undefined.

What is going on? This is too basic to be a compiler bug. If there was another i in a parent scope, there would have been no compilation error. Am I missing something? I am using Visual Studio 2015.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
johngreen
  • 2,676
  • 5
  • 32
  • 47
  • 2
    What compilation flags are you using? (e.g.: do you have `/permissive` and/or `/Za` turned on?) – UnholySheep Apr 11 '18 at 08:31
  • 2
    Seems like trace residue of the things described here https://stackoverflow.com/questions/49447957/for-loop-inside-its-own-curly-braces – StoryTeller - Unslander Monica Apr 11 '18 at 08:33
  • 1
    It's a Microsoft compiler "extension". It's enabled by default for historical reasons. – molbdnilo Apr 11 '18 at 08:36
  • 1
    Do you have the `/Zc:forScope-` (" Force Conformance in For Loop Scope" in the IDE's C++ properties) option set? That causes the MC++ 2015 compiler to use legacy behavior that extends the scope of `for` loop variables. – Michael Burr Apr 11 '18 at 08:43
  • This is definitely not *standard* behaviour and you most certainly shouldn't ever count on it. Your compiler with your current flags may accept this, but another compiler, or even just different versions or flags with the same compiler, may not (and honestly, should not imo) – Zinki Apr 11 '18 at 08:43
  • declarations in for() becoming local was relatively recent change in C and C++, certain compilers still follow older rule I think , stems from the way how for()'s equivalent was defined in documentation. – Swift - Friday Pie Apr 11 '18 at 08:44
  • @Zinki It's a deprecated behavior, quite a lot of older codebase (mostly C though) follows that. All commercial compilers on Windows platform support it as far as I know (MS, Intel, Lahey, PGI...) – Swift - Friday Pie Apr 11 '18 at 08:46

1 Answers1

9

Visual Studio in the past had the feature that extended the lifetime & accessability of variables declared in the for(...) construct (a holdover from plain C behaviour, before the C++98 standard came into being). This behaviour was enabled by default in the older projects.

Microsoft realized that this (for C++) non-standard conforming behaviour might be undesirable and provided the /Zc:forScope compiler option to control this behaviour (and more recently enabled this switch by default, restoring standard C++ conformity).

Check if the /Zc:forScope is set in your project settings under the C++ -> Language rider. If not, set it.

Note: You also have the option to set /Zc:forScope- there to explicitly enable the non-standard behaviour, in case you have legacy code that relies on it.

CharonX
  • 2,130
  • 11
  • 33
  • 1
    Not just VS - but gcc too. It's from before there was a standard. See https://stackoverflow.com/questions/49447957/for-loop-inside-its-own-curly-braces/49448101#49448101 – UKMonkey Apr 11 '18 at 09:20
  • Ah, I wasn't aware of that; it is just one those things that can give you the nastiest of surprises (had a very "interesting" bug with a typo in a variable name a long while back - unfortunately the typo matched up with a variable declared in a for(...) earlier. Was quite "fun" trying to find that one.) – CharonX Apr 11 '18 at 09:25
  • I had a go at editing this to remove the implication that MS made a mistake that it was slow to remedy. But I tired of it. There was a time (before C++98?) when the old, C style behavior was the only behavior. The original Cfront (which I used) did it that way. When the standard changed, Microsoft and others were quick to adopt it, but made the old behavior the default, in order not to break existing projects. It was the right thing to do. – Jive Dadson Apr 11 '18 at 09:44
  • @Jive Dadson I've updated my answer to reference that. I just wish they would not have enabled it by default for the C++ projects too (At least I recall using VS2003 - again a long while back -, creating a C++ project and being confused by it) – CharonX Apr 11 '18 at 10:29
  • I was looking over some 17 year old code that fails to compile due to this very behavior. I could not understand why it was compiling in the past. I am surprised that the original author ignored the scoping standard of C++. – Robert G. Schaffrath Aug 16 '20 at 14:32