1

Lets say I have a loop that inputs a value from user, and if the value is equal to zero, it breaks.

Is there a way to do this without writing the same condition twice?

for example:

int x;

do
{
    std::cin >> x;
    
    if (x)
    {
        //code
    }

} while(x);

What is the cleanest way to do this?

5 Answers5

2

It's probably cleanest to write a little function to read the value, and return a boolean to indicate whether you read a non-zero value, then use that function:

bool read(int &x) { 
    std::cin >> x;
    return std::cin && (x != 0);
}

while (read(x)) {
    // code to process x
}
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 1
    Yup, clean and well-defined behaviour in all C++ standards. Note that `read` would probably be in an anonymous namespace or similar construct. – Bathsheba Jan 26 '22 at 09:46
1

The most laconic way (and note how it tests the integrity of the input stream) is

while (int x; std::cin >> x && x){
    // code
}

Another approach, which gives you a bit more scope for introducing code for the fail condition, is

for (;;){ // infinite loop idiom
    int x;
    if (std::cin >> x && x){
        // code
        continue; // i.e. go round again
    }
    // ToDo - code here?
    break;
};

is one way. This is not to everyone's taste although the break; before the end of the loop body gives some comfort that the loop is not really infinite.

It also has the advantage that the scope of x is not leaked to the outer scope.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • `while (true)` is a much more obvious infinite loop. – user673679 Jan 26 '22 at 09:33
  • @user673679: Not really, compilers emit warnings for that, and it's bad form to suppress them. `for(;;)` is idiomatic C and C++. – Bathsheba Jan 26 '22 at 09:33
  • What compiler? You felt the need to write a comment for your loop, which shows it's not obvious. – user673679 Jan 26 '22 at 09:37
  • @user673679: It's not obvious to the youngsters coming out of MIT - but it is obvious to folk who grew up programming in the formative years of C and C++. – Bathsheba Jan 26 '22 at 09:38
1

When you write the code exactly as you described it with words it get's simpler:

int x;

while(std::cin >> x) // I have a loop that inputs a value from user, and ...
{
    if(x == 0)       // if the value is equal to zero, ...
    {
        break;       // it breaks.
    }

    // do something with x ...
}

The reason for having std::cin >> x; as condition is to stop reading when invalid input is entered or the stream ends.

Lukas-T
  • 11,133
  • 3
  • 20
  • 30
  • 1
    Thank you for your answer. This way looks really clean, but I know a lot of programmers don't like to use "break", including my teacher (don't know why). I am looking for a way without it. –  Jan 26 '22 at 09:24
  • 1
    `while(int x; std::cin >> x)` would be even more beautiful. – Bathsheba Jan 26 '22 at 09:24
  • 2
    @אמיריפה: time for you to convince your teacher of the merits of `break`. – Bathsheba Jan 26 '22 at 09:25
  • Yes, breaks should be avoided. They are actually unconditional jumps (which we all know are bad since 1968) and it will force you to surround it with a condition, which is not so bad, but many-many times it just gives you an extra test case to test or an extra thing to worry. – Florin C. Jan 26 '22 at 09:37
  • 4
    @FlorinC.: I disagree with this strict interpretation of goto's considered harmful. Early loop exits are clean. In some cases, cascaded exits are just better than awfully nested conditions. –  Jan 26 '22 at 09:41
  • @YvesDaoust: that is the title of the article, don't take it literally. Yes indeed, there are some cases when early exits are better. However, the point is: avoid it when you can and in this case it can. – Florin C. Jan 26 '22 at 09:43
0

Verbatim "a loop that inputs a value from user, and if the value is equal to zero, it breaks."

while (true)
{
  std::cin >> x;
  if (x == 0)
    break;
  ...
}
  • This is much cleaner than bunging everything into the loop condition. – user673679 Jan 26 '22 at 09:32
  • @user673679: And you are willing to suppress compiler warnings. Although I like this answer as it is such a literal translation of what you want to do, and therefore readable. – Bathsheba Jan 26 '22 at 09:34
  • Note of course that the behaviour is undefined if `std::cin >> x` fails prior to C++11, as you have a subsequent read of an uninitialised `x`. My way (forgive the plug) using `&&` (which is sequenced and short-circuit) works in all standards. Although of course my declaration within the conditional is C++17 onwards only. – Bathsheba Jan 26 '22 at 09:45
  • I don't want to clutter the answer with considerations on `cin` failures. –  Jan 28 '22 at 16:37
0

How about:

int x;
while (std::cin >> x, x) {
    std::cout << x*5 << std::endl;
}

No ifs, no breaks, the x is already evaluated to be non-zero by the while condition.

Florin C.
  • 593
  • 4
  • 13
  • 1
    Did you test this code? ... If a `0` is entered, it doesn't end the loop. – Damien Jan 26 '22 at 09:52
  • @Damien: thanks for pointing it out. i have tested it on https://code.sololearn.com/ which stops the program when the input ends. corrected it – Florin C. Jan 26 '22 at 09:59
  • It doesn't work for me, doesn't detect that input ends. It works with `while (std::cin >> x && x)` – Damien Jan 26 '22 at 10:08
  • `std::cin >> x, x` is a poor cousin to `std::cin >> x && x` as the former is a read of uninitialised `x` (undefined behaviour) in older C++ standards. – Bathsheba Jan 26 '22 at 10:09
  • Really? https://stackoverflow.com/questions/40773412/order-of-execution-in-an-expression-with-commas-in-c – Florin C. Jan 26 '22 at 10:12