Although it is possible to chain many if-else statements together, this is both difficult to read and inefficient. Consider the following program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <iostream> void printDigitName(int x) { if (x == 1) std::cout << "One" else if (x == 2) std::cout << "Two"; else if (x == 3) std::cout << "Three"; else std::cout << "Unknown"; } int main() { printDigitName(2); return 0; } |
While this example isn’t too complex, x
is evaluated three times, and the reader has to be sure that it is x
being evaluated each time (not some other variable).
Because testing a variable or expression for equality against a set of different values is common, C++ provides an alternative conditional statement called a switch statement that is specialized for this purpose. Here is the same program as above using a switch:
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 |
#include <iostream> void printDigitName(int x) { switch (x) { case 1: std::cout << "One"; return; case 2: std::cout << "Two"; return; case 3: std::cout << "Three"; return; default: std::cout << "Unknown"; return; } } int main() { printDigitName(2); return 0; } |
The idea behind a switch statement is simple: an expression (sometimes called the condition
) is evaluated to produce a value. If the expression’s value is equal to the value after any of the case labels
, the statements after the matching case label
are executed. If no matching value can be found and a default label
exists, the statements after the default label
are executed instead.
Compared to the original if statement
, the switch statement
has the advantage of only evaluating the expression once (making it more efficient), and the switch statement
also makes it clearer to the reader that it is the same expression being tested for equality in each case.
Best practice
Prefer switch statements
over if-else chains when there is a choice.
Let’s examine each of these concepts in more detail.
Starting a switch
We start a switch statement
by using the switch
keyword, followed by parenthesis with the conditional expression that we would like to evaluate inside. Often the expression is just a single variable, but it can be any valid expression.
The one restriction is that the condition must evaluate to an integral type (see lesson 4.1 -- Introduction to fundamental data types if you need a reminder which fundamental types are considered integral types). Non-fundamental types that are convertible to an integer (e.g. enumerated types and some classes) are also valid. Expressions that evaluate to floating point types, strings, and other non-integral types may not be used here.
Following the conditional expression, we declare a block. Inside the block, we use labels to define all of the values we want to test for equality. There are two kinds of labels.
Case labels
The first kind of label is the case label, which is declared using the case
keyword and followed by a constant expression. The constant expression must either match the type of the condition or must be convertible to that type.
If the value of the conditional expression equals the expression after a case label
, execution begins at the first statement after that case label
and then continues sequentially.
Here’s an example of the condition matching a case label
:
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 |
#include <iostream> void printDigitName(int x) { switch (x) // x is evaluated to produce value 2 { case 1: std::cout << "One"; return; case 2: // which matches the case statement here std::cout << "Two"; // so execution starts here return; // and then we return to the caller case 3: std::cout << "Three"; return; default: std::cout << "Unknown"; return; } } int main() { printDigitName(2); return 0; } |
This code prints:
Two
In the above program, x
is evaluated to produce value 2
. Because there is a case label with value 2
, execution jumps to the statement underneath that matching case label. The program prints Two
, and then the return statement
is exited, which returns back to the caller.
There is no practical limit to the number of case labels you can have, but all case labels in a switch must be unique. That is, you can not do this:
1 2 3 4 5 6 |
switch (x) { case 54: case 54: // error: already used value 54! case '6': // error: '6' converts to integer value 54, which is already used } |
The default label
The second kind of label is the default label (often called the default case), which is declared using the default
keyword. If the conditional expression does not match any case label and a default label exists, execution begins at the first statement after the default label.
Here’s an example of the condition matching the default label:
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 |
#include <iostream> void printDigitName(int x) { switch (x) // x is evaluated to produce value 5 { case 1: std::cout << "One"; return; case 2: std::cout << "Two"; return; case 3: std::cout << "Three"; return; default: // which does not match to any case labels std::cout << "Unknown"; // so execution starts here return; // and then we return to the caller } } int main() { printDigitName(5); return 0; } |
This code prints:
Unknown
The default label is optional, and there can only be one default label per switch statement. By convention, the default case
is placed last in the switch block.
Best practice
Place the default case last in the switch block.
Taking a break
In the above examples, we used return statements
to stop execution of the statements after our labels. However, this also exits the entire function.
A break statement (declared using the break
keyword) tells the compiler that we are done executing statements within the switch, and that execution should continue with the statement after the end of the switch block. This allows us to exit a switch statement
without exiting the entire function.
Here’s a slightly modified example rewritten using break
instead of return
:
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 |
#include <iostream> void printDigitName(int x) { switch (x) // x evaluates to 3 { case 1: std::cout << "One"; break; case 2: std::cout << "Two"; break; case 3: std::cout << "Three"; // execution starts here break; // jump to the end of the switch block default: std::cout << "Unknown"; break; } // execution continues here std::cout << " Ah-Ah-Ah!"; } int main() { printDigitName(3); return 0; } |
The above example prints:
Three Ah-Ah-Ah!
Best practice
Each set of statements underneath a label should end in a break statement
or a return statement
.
So what happens if you don’t end a set of statements under a label with a break
or return
? We’ll explore that topic, and others, in the next lesson.
![]() |
![]() |
![]() |
Excellent explanation :)
Just one error:
Introductory section:
> The idea behind a switch statements is simple:
"statements" should be singular
In the first example, there's a semicolon missing at the end of the first true_statement.
Hi. Thanks for this material, it's really awesome what you're accomplishing here.
I didn't understand one thing though Can you help me?
In the enum class chapter, you mentioned that "With enum classes, the compiler will no longer implicitly convert enumerator values to integers.", but in this chapter there is an example in Case Labels section that points:
So, if the compiler does not implicitly convert enumerator values to integers, how is this happening in the switch statement? It seems to me that, for it to compare Color::blue with 4 it should be occuring an implict convertion.
Can you clarify this, please?
Thank you so much!
hi!
`Color` used to be an `enum` in this lesson. The example is no longer accurate. I've added a comment to make it clear that the example only works if `Color` is an `enum`. Sorry for the inconvenience!
No problem at all. Things like that happen when making updates, but this material is still the best "option" for a book, which can be updated as things evolve.
Thanks for clarifying.
.
Question #1,case '/' and '%' shall consider the condition when the variable y is zero. It will cause an error.
Here's my program for question #1:
If the user enters an invalid operator the program will print (example):
Enter the first integer: 2
Enter the mathematical operation: !
Enter the second integer: 5
Error. ! is not a mathematical operation.
2!5 is equal to: 0
I then included <cstdlib> and put std::exit(0) after the std::cout statement in the default switch case (replacing the statement "return 0;"), to get rid of the unnecessary last printed line.
I know this is not "best practice".
Also your solution prints that unnecessary line and I think this is not what we want.
For Quiz 2:
I didn't use getAnimalName() but I managed to get the result.
Is this acceptable?
May I ask why we want to use getAnimalName()? It's very confusing to write another function.
Is getAnimalName() to be combined with other structs?
If the final result is what was expected, it's probably acceptable. But you should think more about "is it the best way?".
This is a simple example, so I agree with you that the function getAnimalName() is not worth it.
But think broadly, as each function has a goal: getAnimalName is supposed to retrieve the animal name, and printNumberOfLegs is supposed to print the number of legs of the animal. They have different goals.
Imagine you have another functions: one for type of food each animal eat, one for determining its family (mammal or bird), one to determine if it gives milk, one to determine if you can eat it.
You would have to type the animals names in all of them, which could take more time to type. If you use the function, the IDE has the option for auto complete, which will save a lot of time when writing huge codes.
So, think of these examples as a way to show you how things work, but try to expand them to a more complex solution. Then you can evaluate if your answer is "acceptable".
I don't know if it's the best way, no one tells us why.
There are a few problems.
1. Without a Use case, we do not know what it is for.
2. The methodology is inconsistent. You learn 1 method in Chapter 3, then in Chapter 4, the Chapter 3 method is redundant. E.g. Lambda calculus, for-range loops. Even in the above Quiz, is it worth remembering?
Based on my understanding, I wrote it in one function.
Is this acceptable for Q1?
If you're going to write everything in a single function there's no point in having any function other than `main`. You're making it impossible to reuse code.
Ah, apologies.
I will break it down.
So in the section about why we can use the variables declared inside a case statement, "Variable declaration and initialization inside case statements", you say that local variables are "created" at the beginning of a block, but can not be seen (scope) until the point of declaration. However, a few sections back when you introduced to topic of automatic storage duration you made the case that local variables have automatic storage duration in that they are created at definition and destroyed at the end of their respective block. But, now you make the case that they are created at the start of a block, not necessarily at the point of declaration. I'm just a little confused, which is it? Obviously I'm missing something here.
Do you mean that a local variable that is declared but not initialized is created at the start of a block? As opposed to a variable that is declared and initialized that is created at definition? Thanks for any help!
That paragraph went beyond the scope of this tutorial, I've adjusted it.
Storage for local variables with automatic storage duration is created before or when execution reaches the variable's declaration. When the storage is actually created is not specified (It happens before the variable is first used).
From a language-point-of-view, variables with static storage duration are usable after their declaration and are initialized when their declaration is executed.
Even though the previous lesson isn't entirely correct about this, I don't want to adjust it, because getting into low-level details would be more confusing than changing the description of variables with automatic storage duration here.
Please take this lesson on a higher beauty level by placing the "Warning: Forgetting the break statement at the end of the case statements is one of the most common C++ mistakes made!" line in a pretty Warning box.
Hi! Thanks so much for all the tutorials!
What reason to not do it this way?
Hello,
In a quiz thread question 1 you made a mistake.
std::cout << "Enter a mathematical operator (+, -, *, /, or %): ";
char operation{}
std::cin >> operation;
the char operation without ; so, we can't compile the programm
to fix it we need to add ;
Have a nice day
Lesson fixed, thanks!
how can i improve this program?
You don't need `temp`, you can
Great chapters. A step up from the normal tutorials (about the same subjects as these chapters), we get on the web and youtube, which is what I'm looking for. I like the application of what we have previously learned. Notes in code with some slight additions to test ideas form prior chapters. Code below, cheers.
An auto-formatter fixes the indentation for you.
If multiple cases in a switch do the same, you can let them fall-through
Thanks. I've seen the solution and never thought about the fall through, but thought it was pretty cool. I copied it so I can experiment with using code the way the solution had it. I liked how there was only one call in main to print the outcome so I'm going work with mine to try and get the same "format", thanks.
Hi,
Can the following be a good solution for the question #1?
yes
Thank you so much.
"However, initialization of variables directly underneath a case label is disallowed and will cause a compile error. "
I think there is an exception that if the case label is the last one (no 'default') we can both declare and initialize a variable.
What value should be passed to the function as an argument to cause 'default label' to be executed? I mean color is of enum class type. How come we can give another value when it is not defined inside enum class Color? Like if it was given the argument 'Color:pink", which doesn't exist, we would get compile error!
An enum is a fancy integer. Every value that is valid for the underlying type (usually `int`), is a valid value for for an enum type.
There is no enumerator to represent 12345, but it's a valid value.
Great!
Thank you so much
I didn't want to write two switch functions, so:
#include <iostream>
enum class Animal
{
pig,
chicken,
goat,
cat,
dog,
ostrich
};
struct animalData
{
Animal species{ Animal::ostrich };
int numLegs{ 0 };
std::string name{ "noname" };
};
animalData setAnimalData(Animal animal)
{
animalData thisAnimal;
switch( animal )
{
case Animal::pig:
thisAnimal.species = animal;
thisAnimal.numLegs = 4;
thisAnimal.name = "Pig";
return thisAnimal;
break;
case Animal::chicken:
thisAnimal.numLegs = 2;
thisAnimal.name = "Chicken";
return thisAnimal;
break;
case Animal::goat:
thisAnimal.numLegs = 4;
thisAnimal.name = "Goat";
return thisAnimal;
break;
case Animal::cat:
thisAnimal.numLegs = 4;
thisAnimal.name = "Cat";
return thisAnimal;
break;
case Animal::dog:
thisAnimal.numLegs = 4;
thisAnimal.name = "Dog";
return thisAnimal;
break;
case Animal::ostrich:
thisAnimal.numLegs = 2;
thisAnimal.name = "Ostrich";
return thisAnimal;
break;
default:
thisAnimal.numLegs = 0;
thisAnimal.name = "Undefined";
return thisAnimal;
break;
}
}
int main()
{
animalData newAnimal1{ setAnimalData(Animal::cat) };
animalData newAnimal2{ setAnimalData(Animal::chicken)};
std::cout << "A " << newAnimal1.name << " has " << newAnimal1.numLegs << " legs\n";
std::cout << "A " << newAnimal2.name << " has " << newAnimal2.numLegs << " legs\n";
return 0;
}
Thanks!!
Now you can't do one or the other, you have to do both. If you know that in your program you'll always need the name and number of legs at the same time, that's fine.
`return thisAnimal` is unconditional, it shouldn't be in the `switch`-statement, same for the species. You also don't need to `break` after a `return`, a `return` stops everything. Otherwise good job :)
PS: Please use code tags.
As an academic exercise, any improvements. Is this how one would go about using an enum (with user input) to achieve something like this? I realise it's unnecessary to have used enum class for the operator having now seen your solution.
Thank you in advance, this website is a god-send.
`getUserOperator` should return the `Operation` right away, not an `int`. You also don't need `selected`. Execution inside a function stops when you `return`.
How do I make it such that if there is an error, it'll go back and give the user another chance to input the operator? And with "getUserOperator", how do you return the operator straight away as it's in an enum Class?
I get that the easy way is to just return a char as per the solution, just want to understand these enums better
and
Assignment help globally at all level academic. We work 24*7 for your convenience, so you don’t need to wait for any certain time, you can reach any time at our site, for any Assignment Help directly get into touch with our expert write college homework for me, you can concern directly with our Assignment Expert. UK assignment help websites in uk me at any time with good service hire essay helper online.
How Color::blue evaluates to 4?
Enumerators are integers in disguise. They're numbered starting at 0 and counting up.
why is this not working anyone know?
The debugger should help you fix these problems.
I made a few changes and added comments where it went wrong, I think the main issue was a missing brace, which made the main function contained inside of the calculate function. Aside from that, you should use breaks inside of the switch function. Try and make your formatting more consistent using '\n' for readability. Hope this helps!
You don't need to `break` after a `return`, a `return` stops everything.
Ahh okay, that makes sense!
Thank you for the clarification!
my take on question #2, should be improved somewhere?
`printNumberOfLegs` doesn't do what it's supposed to do. We can do things like
That's a very weird cat.
The signature should be
Give that a try, you're not too far off.
As a note to your current `printNumberOfLegs`: Line 41 and 45 are identical. You can let the `case 2` fall through to `case 4`.
Quiz 1
Quiz 2
Avoid abbreviations, they make your code harder to understand. Otherwise good job!
I'm assuming you're talking about "lval" and "rval?"
Yep, and "op"
But in your solution for question 1) you've used "op" ?
Thanks for pointing this out! There was a bunch of outdated code in this lessons, I gave it an overhaul. There's no need to re-read the lesson, the content stayed the same.
I think I got the lesson understanding in general but still confused of declaration and initialization. Please clarify me why this code not working (((
Correct.
If we use a variable with an initializer, we're safe to assume that the variable has been initialized.
If line 7 was legal, there'd be a variable with an initializer that has not been initialized.
Why are use using break statements after "default" in the examples? Isn't that implicit - provided default is added at the last?
Yes, but by having the `break` there we can move the default case around or add other cases after it.
My answer for question #2
Name variables descriptively. Your names are useless.
I've been reading comments, and I want to make sure I did understand why an initialization is not permitted inside a switch statement.
Initialization asks for two simultaneous things that, in this context, have different degree of contingency: declaration must happen, but you cannot ascertain the assignment will be performed since the execution might never get to that statement, so the engagement of having to do both simultaneously is like saying to the compiler "the program might need to do this and at the same time it should not". Nevertheless, separating declaration from assignment avoids this conflict: the assignment might never happen, but that's OK since thanks to its own statement the declaration will happen anyways.
That's it.
here Control flow jumps from switch line to the first case line
so how we could we define a without decleration since compiler will never see int a ;
The compiler sees "int a". The jump doesn't occur until runtime.