Contents

Chapter 7

Deciding Things: if, else, and Booleans

Comparison, and, or, and chains that stay readable.

Every program you’ve written runs every line, top to bottom, whatever happens. It can’t skip anything and it can’t choose.

That changes here. And three earlier chapters have been waiting for this one: chapter 3’s bool finally does something, chapter 4’s npos becomes usable, and chapter 6’s warning about comparing decimals comes due.

Asking a question

int age = 20;

if (age >= 18) {
    std::cout << "You can vote.\n";
}

The thing in the brackets is a condition: something that is either true or false. If it’s true, the code in the braces runs. If it isn’t, the whole block is skipped as though you never wrote it.

There are six ways to compare:

OperatorAsks
==are these the same?
!=are these different?
<is the left one smaller?
>is the left one bigger?
<=smaller or the same?
>=bigger or the same?

And here’s what chapter 3’s odd little type was for. A comparison produces a bool:

bool can_vote = (age >= 18);
std::cout << can_vote << '\n';
1

Still printing 1 rather than true, as chapter 3 warned. But now you can put that bool straight into an if, which is what it was always for.

The mistake everyone makes once

Chapter 3 said = and == are different operators. Here’s what happens when you reach for the wrong one:

int age = 20;

if (age = 0) {
    std::cout << "this ran\n";
} else {
    std::cout << "this ran instead\n";
}

std::cout << "age is now " << age << '\n';
this ran instead
age is now 20

Except that isn’t what it prints. It prints:

this ran instead
age is now 0

Read the condition again. age = 0 doesn’t ask whether age is zero. It sets age to zero, and then hands the result to the if. Your variable is destroyed as a side effect of asking a question you never actually asked.

Otherwise

else catches everything the if didn’t:

if (age >= 18) {
    std::cout << "You can vote.\n";
} else {
    std::cout << "Not yet.\n";
}

And else if chains several questions together, checked in order until one fits:

if (age < 5) {
    std::cout << "Free\n";
} else if (age < 18) {
    std::cout << "Child\n";
} else if (age < 65) {
    std::cout << "Adult\n";
} else {
    std::cout << "Senior\n";
}

Order matters enormously. C++ takes the first branch that’s true and skips the rest, so a 3-year-old never reaches the age < 18 test, even though that’s also true of them. Write the narrowest cases first, or the broad ones swallow everything.

Combining questions

Three operators let you ask about more than one thing:

if (age >= 18 && member) {
    std::cout << "Adult member\n";
}

if (age < 5 || age >= 65) {
    std::cout << "Reduced price\n";
}

Read them out loud, “age is at least 18 and member”, and they’re exactly as obvious as they look. Use brackets when you mix && and ||, for the same reason you used them for arithmetic: nobody wants to look up precedence rules.

Two comparisons that surprise people

Text compares the way you’d hope.

std::string a = "ada";
std::string b = "ada";
std::cout << (a == b) << '\n';
1

== on two std::strings compares the actual characters. That sounds unremarkable until chapter 24, where you’ll find that the older kind of C++ text does something completely different and far worse. This is std::string being kind to you.

< and > work too, comparing alphabetically, with a catch. "Ada" < "ada" is true, because capital letters come before lowercase ones in the character codes. Sorting names case-insensitively is a job you have to do deliberately.

Decimals do not compare the way you’d hope.

double x = 0.1 + 0.2;
std::cout << (x == 0.3) << '\n';
0

False. Chapter 6 showed why: 0.1 + 0.2 is really 0.30000000000000004, and that is not 0.3. Never compare two doubles with ==. Ask whether they’re close enough instead: the difference between them, ignoring the sign, being smaller than some tiny amount you choose.

Finding out whether something was found

Chapter 4 left you with a cliffhanger: find returns an enormous number when it fails, called std::string::npos, and you had no way to act on it. Now you do:

std::string full = "Ada Lovelace";
auto space = full.find(' ');

if (space == std::string::npos) {
    std::cout << "No space in that name.\n";
} else {
    std::cout << "Space at position " << space << '\n';
}

That’s the pattern you’ll use every time you search for anything: search, then check whether it was found, and only then use the position.

Exercise 1 · Break your own ticket rules

Write the age chain from earlier, but put the branches in the wrong order, then test age < 65 before age < 18.

Run it with an age of 12 and watch a child get charged as an adult. No error, no warning; the program is doing exactly what you told it. Then put the order back and notice that you now understand why it has to be that way round.

Check yourself

1. What does if (age = 0) do when age is 20?

Not quite. That is what == would do. A single = assigns rather than asks.

Yes. Your variable is wrecked as a side effect. The compiler warns about it with -Wparentheses, but it still builds.

Not quite. It is legal C++, since assignment produces a value, and a value can be a condition. That is precisely why it is dangerous.

2. Why should you never write if (x == 0.3) for a double?

Not quite. It compiles and runs on doubles perfectly well. That is the problem. It answers a question you did not mean to ask.

Yes. 0.1 + 0.2 is really 0.30000000000000004. Compare the difference against a small tolerance instead.

Not quite. Nothing undefined here. It reliably gives you a correct answer to the wrong question.

3. In an else-if chain, a 3-year-old is tested against age < 5 first and matches. What happens to the later age < 18 test?

Not quite. Only one branch of a chain ever runs. Once something matches, the rest are skipped entirely.

Yes. Which is why order matters: put the narrowest cases first or the broad ones swallow them.

Not quite. Overlapping conditions are normal and useful. The compiler has no way to know which order you meant.

Project

Ticket pricing with real rules

Roughly 45 minutes

A cinema charges by age, with a weekend surcharge. Ask for the details and print the price.

The rules:

  • under 5: free
  • 5 to 17: £6
  • 18 to 64: £10
  • 65 and over: £7
  • weekends add £2, but a free ticket stays free
Age? 12
Weekend? (y/n) y
Price: 8

Read the weekend answer into a char with std::cin >> weekend; and compare it with 'y' in single quotes: one character, exactly as chapter 4 described.

Check yours against these:

AgeWeekendPrice
3n0
3y0
12y8
30n10
70y9

The third and fifth rows are the ones that catch people. The surcharge is a separate decision from the age band, and it needs && to avoid charging a toddler £2 for the privilege of being free.

Stretch: add a members’ discount of 10% off the final price, and decide for yourself whether it applies before or after the weekend surcharge. There’s no right answer, but there is a right way to find out, which is to write both and compare the numbers.