117

I have come across this for-loop layout:

#include <iostream>
int main()
{
    {
        for (int i = 0; i != 10; ++i)
        {
            std::cout << "delete i->second;" << std::endl;
        }
    }

    {
        for (size_t i = 0; i < 20; ++i)
        {
            std::cout << "delete m_indices[i];" << std::endl;
        }
    }
    return 0;
}

I was wondering what this extra layer of braces is for? This appears a few times in our code base.

Muntasir
  • 798
  • 1
  • 14
  • 24
Ed Norman
  • 1,108
  • 1
  • 9
  • 16

4 Answers4

287

Once upon a time, many moons ago, VS6 existed and was popular. It failed however to conform to a number of C++ standards; which was reasonable at the time as it was released just before (on the same year) the standard was officially released; it did however adhere to the draft of the standard as far as I'm aware.

One of the standards that changed between the draft and the official standard, was the lifetime of for loop variables created in the first section; leading to the following code failing to compile

{
    for (int i=0; i<1; ++i){}
    for (int i=0; i<2; ++i){}
}

because i was redefined by the second for loop.

While other compilers also suffered this bug; I highlight the VS6 one because it remained the only version of visual studio for a number of years after the release of the standard, but never released an update for this particular issue; meaning that it had a more significant impact.

A solution to this is to force the whole for loop into its own scope as you have shown.

Jaden Travnik
  • 1,107
  • 13
  • 27
UKMonkey
  • 6,941
  • 3
  • 21
  • 30
  • 49
    No need to find VS6 to see that @bolov, set "Force Conformance in For Loop Scope" to "No" in VS2015, and enjoy ;-) – alain Mar 23 '18 at 11:08
  • 5
    @alain "option 'Zc:forScope-' has been deprecated and will be removed in a future release" and compiles without a problem... I am sad – bolov Mar 23 '18 at 11:34
  • 7
    GCC prior to version 2.7 also exhibited this behaviour. See https://docs.freebsd.org/info/g++FAQ/g++FAQ.info.for_scope.html – Jeremy Mar 23 '18 at 14:40
  • 5
    @Damon it wasn't when VS6 was first released; however when the standards changed, an update that conformed to them was never released. VS6 remained in popular for a good few years after the standards were changed. – UKMonkey Mar 23 '18 at 16:20
  • 4
    @UKMonkey ISO/IEC 14882:1998 was released after VS6, so it's not so much a question of that the standards have _changed_ so much as there was no real C++ standard before that, just more or less compatible implementations. – Cubic Mar 23 '18 at 16:56
  • 3
    @Cubic - But years before the first standard was published there were committee drafts available. And, when implemented, VS6 for-loops conformed to those drafts. Then something happened. – Bo Persson Mar 23 '18 at 17:40
  • 5
    It's definitely not just VS6, I remember working with a Symantec compiler back in the days that had this as well. So we have already VS6, GCC, and Symantec - that sounds like this behavior once **was** really popular... – cmaster - reinstate monica Mar 24 '18 at 21:00
  • 7
    Attributing this to a sin of an old Microsoft compiler is bogus. This behaviour was actually a feature of draft C++ standards, and a number of compilers did this (not only Microsoft compilers). From memory, it was changed in a draft during about 1995 to make the variable local to the loop - about three years before the first C++ standard was ratified. So most C++ compilers predating (about) 1996 worked this way. – Peter Mar 25 '18 at 13:26
  • 3
    +1 but i think the answer should really point out that it was quite ok to behave like that. In its current form it indicates that it was a bug which wasnt the case – BudBrot Mar 25 '18 at 17:23
  • 3
    @LightnessRacesinOrbit "such antique code"? "Antique" code is everywhere, and so it should be. The mindset of the developer should be for his code to be around for a long time. We may fire a dev at our company because he can't rid himself of the "quick and temporary" mindset in all his work. The common part of human DNA is much older than VS6, but it's execution (on equally antique hardware, namely ribosomes) produces the proteins we need to live every day. Maybe you'd be happy to discard that code because it's really **really** "antique" (much older than VS6), with fatal results to yourself? – Reversed Engineer Mar 26 '18 at 06:07
  • @HappyGreenKidNaps That was entirely the problem; VS6 was the only version of VS for a long time; which meant for commercial windows development, the number of compilers you could switch to were somewhat limited. It meant that the amount of code that had to deal with this grew for a lot longer than it needed to. – UKMonkey Mar 26 '18 at 08:57
  • @Joker_vD: Not just MS. I'll be the first person to point a finger at MS, but here they're really innocent (or not at fault alone). It was a major surprise for me when GCC suddenly started telling me my code which had been good for years was wrong. This included code that compiled without error on GCC previously, code that compiled with Borland no problemo, and code that I had written on the Atari ST / Megamax C. Which, of course, compiled just fine. – Damon Mar 26 '18 at 12:12
15

{ and } will create a scope and if you define some variables in the scope you cannot access them from outside. But for already create that scope. So

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

is the same as

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

but if you define something between them, there is a difference

{int a = 0; for(int i = 0; i < count; ++i){}}

In this example, a won't be accessible from outside scope.

pensono
  • 336
  • 6
  • 17
cokceken
  • 2,068
  • 11
  • 22
2

It's a block scope marked by {} braces. It is usually used to mark the area of automatic storage. In your case it doesn't seem to do anything as the for loop has its own scope in standard C++.

Ron
  • 14,674
  • 4
  • 34
  • 47
2

In your particular example there is no reason for them.

Sometimes you could want to create a scope for a variable:

float average;
// ...

{
int sum = 0;
for (int i = 0; i < count; ++i)
{
   sum += v[i];
}
average = (float)sum / count;
}

// use average
// sum not in scope here

However I see this an anti-pattern. Usually if you find yourself in need of doing this then most likely the for should be its own function.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • Okay if you think it should be in its own function (I can think of many times where that would only add overhead at the very least but I'm not going to go there) a hypothetical question for you: what about if you need a specific local scope to a switch case? There certainly are times that adding an additional scope (which of course a function does too) (note that for your example I don[t at all think a separate function is a bad idea) is unnecessary but other times it's not that simple even if there are other ways. – Pryftan Jul 10 '18 at 14:23