6

In the code below, I have a while statement used to make sure an input string is less than 10 characters. I've declared a bool called cont which I use to tell the while loop to stop once my conditions have been met.

#include "stdafx.h"
#include <iostream>
#include <string>

int main()
{
    using namespace std;

    cout << "Enter a string less than 10 characters long: ";

    string teststring;

    {
        bool cont(false);

        //if input is 10 or more characters, ask for input again until it is less
        while (!cont)
        {
            getline(cin, teststring);

            if (teststring.length() >= 10)
            {
                cout << "Too long, try again: ";
            }
            else
            {
                cout << "Thank you.\n\n";
                cont = true;
            }

        }
    }

    return 0;
}

As you can see there, I've used a set of {}s to separate the code out, giving the cont variable a local scope within those braces. I did this so that if I ever wanted to use that variable name again, I could just re-declare it, and when I'm done with it, it's destroyed.

Is this an acceptable practice? Or is there a better way to do what I've done? I acknowledge that in this specific, basic scenario, the condition is simple enough that it's hardly necessary, but I may want to do this for more complex loops in the future.

Eric David Sartor
  • 597
  • 2
  • 8
  • 22

4 Answers4

2

In general? Yes. This is good.

In this specific case? No. Needless. You are not using that name again, and in this simple code you're not going to. So it's just noise.

See… it's a balance.

I find myself doing this quite a bit in functions that execute several related SQL statements. Each time I might build it up with a std::stringstream called ss. Sure, I could give each one a different name, but it entirely prevents errors to keep each statement builder in its own scope.

It's also a very common technique when you use things like lock guards.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Agreed that it is pointless in this scenario. I guess what I meant was if I had multiple loops, each doing a different thing, so I couldn't just make one neutral function. But someone mentioned that I could just declare the `bool` inside the condition, which probably makes much more sense. Thanks though! – Eric David Sartor May 28 '15 at 07:10
2

This is an acceptable practice and does exactly what you say. However it is rarely used because in small functions, it is unambiguous and therefore acceptable to have the cont variable in the function top-level scope. If you feel like you need to separate scope in a larger function, creating another function with an explicit name is generally preferred.

You can think of those braces as nameless functions only called once. If you see yourself using it a lot, perhaps you should give it a name of its own.

Another option is to rewrite the loop to not need the cont variable, for example:

string teststring;
do
{
    cout << "Enter a string less than 10 characters long: ";
    getline(cin, teststring);
} while (teststring.length() >= 10);

cout << "Thank you.\n\n";

But this is not always possible, especially if you need to output a different message depending on the stopping condition.

fouronnes
  • 3,838
  • 23
  • 41
2

Yes, it is fine, if you have a good reason to be re-using a variable. Lock guards are the most common usage in my own code, and the example given in Lightness's answer of std::stringstream ss. Basically do this any time that choosing a different variable names feels more awkward. E.g. if you are writing lock1, lock2, lock3, ... in your code.

However, more acceptable practice, would be to regard long function bodies as a code smell, and refactor them into their own functions. E.g.

...
string teststring;

{
    bool cont(false);
    //10 lines of code to handle the foo scenario
}
{
    bool cont(false);
    //15 lines of code to handle the bar scenario
}
...

This is better handled by refactoring to look like:

...
string teststring;
foo(teststring);
bar(teststring);
...

void foo(string &teststring){
bool cont(false);
//10 lines of code
}

void bar(string &teststring){
bool cont(false);
//15 lines of code
}
Darren Cook
  • 27,837
  • 13
  • 117
  • 217
  • 1
    Upvoted for the `std::lock_guard` point. The idea that all critical sections should be plucked out of their 'natural' home in the middle of a function in to a seperate function is madness. – Persixty May 28 '15 at 07:31
  • 1
    That said could I suggest void foo(string& teststring)? Otherwise you introduce temporaries that might be onerous (if teststring were large). – Persixty May 28 '15 at 09:11
  • 1
    @DanAllen I was trying to keep it simple (see item 41 in Effective Modern C++ for the full complexity!)... but just looked at the OPs code and he is modifying `teststring` so, yes, they definitely should've been references! Thanks :-) – Darren Cook May 28 '15 at 09:22
1

This is a toy case obviously but this is a good practice and 'stand-alone' blocks are there for that purpose. I happen to believe they're a better way to structure long functions than breaking it down into members which (in many cases) have no separate purpose.

In such a case you can provide a faster, clearer, safer program particularly if there's a comment (possibly one line) introducing each block.

However in this case you might consider:

    for ( bool cont(false);!cont;)

Which has the same effect. Variables declared in a for(.;.;.) statement are confined to the scope of that statement.

In this case you can dodge the whole variable with:

    for(;;) //<--- Canonical formulation for 'infinite' loop.
    {
        getline(cin, teststring);

        if (teststring.length() >= 10)
        {
            cout << "Too long, try again: ";
        }
        else
        {
            cout << "Thank you.\n\n";
            break; //<--- Escapes the loop.
        }

    }

Footnote (in response to comments):

You should regard a for loop as 'syntactic sugar' on a while loop. That is no different in their performance, etc. and just pick the one that reads best. for(;cond;) just looks funny.

There may be a tiny (tiny) performance benefit to break but I happen to think it's actually simpler and more readable in many cases.

If the code were more complex there might be more 'loop end' code so it becomes:

for(bool cont(false);!cont;) {

    //Complex code with multiple 'exit' conditions...

    if(!cont) {
        //Go round again code that won't fit nicely in last term of for loop...
    }
}

Whereas using break just makes a 'swift exit' easier to understand. They're not (widely) considered to have the 'bad karma' of goto because they 'go to' a very clearly defined point of execution.

Persixty
  • 8,165
  • 2
  • 13
  • 35
  • That's pretty cool actually. So even though there are no conditions, the `else` statement will break the loop anyways. That's great for basic stuff. Is there anything wrong with just declaring the `bool` in a while loop condition? Is there a specific advantage to a for loop over a while? I'm a beginner, so I just want to fully understand it.I figure there could be a scenario where multiple conditions would end the loop, and the `bool` seems more aesthetically pleasing to me. – Eric David Sartor May 28 '15 at 07:16
  • I like this a fair bit. For loops have more functionality anyways, and the option of just breaking out or having multiple completion conditions is great as well. Thanks! – Eric David Sartor May 28 '15 at 08:14