02 - if

The simplest control flow keywords of them all - if. It executes attached code only if the condition is true.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

int main()
{
	std::cout << "Enter a number: ";
	int x = 0;
	std::cin >> x;

	if (x % 2 == 0)
	{
		std::cout << x << " is even (divisible by 2)\n";
	}
}

Comparisons naturally produce values of type bool but the condition can be any expression that is or can be converted to a value of type bool. if works as an explicit convertion so objects which require explicit convertion will also work.

The negative branch can be introduced with else:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

int main()
{
	std::cout << "Enter a number: ";
	int x = 0;
	std::cin >> x;

	if (x % 2 == 0)
	{
		// positive branch - executed only if condition is true
		std::cout << x << " is even (divisible by 2)\n";
	}
	else
	{
		// negative branch - executed only if condition is false
		std::cout << x << " is odd\n";
	}
}

What if I want only negative branch?

Simply reverse the result. You can use ! to flip a boolean but often you can just modify the condition to produce reversed result:

1
2
3
4
5
6
7
// reverse result
// use when the condition's code can not be easily modified
if (!(x % 2 == 0))

// modify condition
// the preferred way - it results in simpler code
if (x % 2 != 0)

You can omit braces ({}) if exactly one statement is used:

1
2
3
4
if (x % 2 == 0)
	std::cout << x << " is even (divisible by 2)\n";
else
	std::cout << x << " is odd\n";

Some projects take the rule even further and always require braces, even for simplest and shortest 1-line statements - the argument for it is safety as braces prevent situations like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

int main()
{
	int x = 6;

	if (x % 2 == 0)
		std::cout << x << " is even\n";

		if (x % 4 == 0)
			std::cout << x << " is divisible by 4\n";
	else
		std::cout << x << " is odd\n";
}

The above program will print that x is both even and odd. The problem is that code has been written assuming that the else will attach to the first if statement, but it attached to the second - formatting does not affect language grammar. Some compilers may print a warning that the code is misleadingly indented:

main.cpp: In function ‘int main()’:
main.cpp:7:2: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
  if (x % 2 == 0)
  ^~
main.cpp:10:3: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘ifif (x % 4 == 0)
   ^~

This is the minimum to make the code work and be consistent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>

int main()
{
	int x = 6;

	if (x % 2 == 0)
	{
		std::cout << x << " is even\n";

		if (x % 4 == 0)
			std::cout << x << " is divisible by 4\n";
	}
	else
	{
		std::cout << x << " is odd\n";
	}
}

Sometimes, thanks to many conditions your code might indent very deeply, like this (image shows PHP, not C++):

code Hadouken

There are ways to solve such problems. One of them does not require any additional features so I can present it to you now. If you have an if-else tree that nests only on one end like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>

int main()
{
	int x = 6;

	if (x % 2 == 0)
	{
		std::cout << x << " is divisible by 2\n";
	}
	else
	{
		if (x % 3 == 0)
		{
			std::cout << x << " is divisible by 3\n";
		}
		else
		{
			if (x % 5 == 0)
			{
				std::cout << x << " is divisible by 5\n";
			}
			else
			{
				if (x % 7 == 0)
				{
					std::cout << x << " is divisible by 7\n";
				}
				else
				{
					if (x % 11 == 0)
						std::cout << x << " is divisible by 11\n";
				}
			}
		}
	}
}

...then you can use the rule that allows a single statement without braces and format the code in a very compact way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

int main()
{
	int x = 6;

	if (x % 2 == 0)
		std::cout << x << " is divisible by 2\n";
	else if (x % 3 == 0)
		std::cout << x << " is divisible by 3\n";
	else if (x % 5 == 0)
		std::cout << x << " is divisible by 5\n";
	else if (x % 7 == 0)
		std::cout << x << " is divisible by 7\n";
	else if (x % 11 == 0)
		std::cout << x << " is divisible by 11\n";
}

Here braces are not only ommited for print statements, but they are also ommited between else and if keywords.

Syntax sugar

Since C++17 it's possible to have an extra statement inside if, before the condition. It's very useful if you need to perform extra work but also limit the scope of any extra objects:

1
2
3
4
5
6
7
8
9
10
if (int x = func(); x == 0 || x == 1)
{
	// do stuff...
}
else
{
	// do other stuff...
}

// x does not exist in this scope

The code is equivalent to:

1
2
3
4
5
6
7
8
9
10
11
12
{
	int x = func();

	if (x == 0 || x == 1)
	{
		// do stuff...
	}
	else
	{
		// do other stuff...
	}
}

Ternary conditional

There is a special ternary (arity of 3) operator that is similar to if but it works on the expression level, not statement. This makes it possible to use it as a subexpression inside complex statements:

1
2
// () are necessary because ?: has normally lower priority than <<
std::cout << "x is " << (x % 2 == 0 ? "even" : "odd") << "\n";

and to simply else-if code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const char c =
	  x == 1 ? 'a'
	: x == 2 ? 'b'
	: x == 3 ? 'c'
	: x == 4 ? 'd'
	: x == 5 ? 'e'
	: 'f';

// equivalent else-if code
char c; // can't use const here
     if (x == 1) c = 'a';
else if (x == 2) c = 'b';
else if (x == 3) c = 'c';
else if (x == 4) c = 'd';
else if (x == 5) c = 'e';
else             c = 'f';

The ?: operator must always have 2 branches. You can get very creative with its usage (it works as a functional subexpression, not as a full statement) (not only for assignments) but I advise you to not overuse it because (due to grammar and backwards compatibility) its evaluation rules have gone extremely complex.

Advanced example

Sometimes it might not be obvious how large simplifications are possible. The example below has a space for multiple potential improvements.

1
2
3
4
if (a == f(b))
	x = std::make_pair(true, a);
else
	x = std::make_pair(false, f(b));

Note that the result of the function is wanted in both branches - the first branch (before improvements) used a local variable to avoid duplicate function calls but this resulted in hiding of the redundancy.

1
2
int r = f(b);
x = std::make_pair(a == r, r);

Exercise

Question 1

What's wrong with the following code?

1
2
3
4
5
6
7
8
if (x != 0);
{
	// do stuff...
}
else
{
	// do other stuff...
}
Answer

There are 2 mistakes:

  • There is ; immediately after the condition (null statement) which acts as the braceless one statement. It will prevent actually intended statements from happening and break the else (either shifting it to a different if or making a compiler error).

  • There are 2 branches but the condition contains a negation. It can be simplified by reversing the condition and swapping branch bodies:

1
2
3
4
5
6
7
8
if (x == 0)
{
	// do other stuff...
}
else
{
	// do stuff...
}

Question 2

What's wrong with the following code?

1
2
if (x = func())
	std::cout << "x is non-zero\n";
Answer

The code uses =, not ==. This will cause the assignment to take place and because assignment operator returns first operand (it's right-to-left associative) the if statement will evaluate x after the assignment (converting it to bool). All major compilers should warn on this type of mistake.

If both the assignment and the test are desired, major compilers agreed on this solution:

1
2
3
// extra set of () indicates desirable assignment in subexpression
if ((x = func()))
	std::cout << "x is non-zero\n";

I don't get the assignment operator returns first operand part. What makes = compile inside if?

You need to understand that if does not see every entity inside the condition, it simply acts as a test of a value of type bool. The whole condition expression is evaluated first and only then its result is brought to the if logic. Why assignments return first operand as the result? This will get clearer once you get familiar with functions and the concept of returning a result - many parts of C++ work on the basis of functions and most operators (including =) too.

Writing

Write a simple pseudo-calculator program:

  • The user should enter 2 numbers.

  • The user should enter extra number specifying operation to perform (addition, subtraction, multiplication, division, modulus).

  • The program should check validity of the operation (division and modulus by 0 have undefined behavior) and execute it if possible. Print the result.