#049 – Using [[fallthrough]] to strengthen switch/case blocks

Consider a program that implements a navigation screen using a switch/case block.

switch (selection)
{ 
	// Option 1
	case kEnterExpenses: enterExpensesForMonth(expenses); break; 
	
	// Option 2
	case kViewMonthlyExpenses: viewMonthlyExpenses(expenses); break;

	default:
		std::cerr << "Invalid selection! Try again\n\n";

Each case ends with a break. This ensures that a case doesn’t bleed into the next case, thereby preventing fallthrough.

When the the customer wants to enter expenses for a month, the program doesn’t immediately prompt the user to view their monthly expenses. The break is intentional. The compiler will warn you if you fail to add it.

However, sometimes fallthrough is desirable.

C++17 added the [[fallthrough]] attribute to let you signal intent: yes, I meant this; please stop warning me, and please tell the next reader

Using [[fallthrough]] for intentional cascading

In the program below, we want the cases to cascade into one another.

#include <iostream>
#include <string>

int main() {
    char size;
    int price = 0;

    std::cout << "Select size: (S)mall, (M)edium, (L)arge: ";
    std::cin >> size;

    switch (toupper(size)) {
        case 'L':
            price += 2; // Large adds $2
            [[fallthrough]];
        case 'M':
            price += 1; // Medium adds $1 (Large also gets this)
            [[fallthrough]];
        case 'S':
            price += 2; // Base price for all sizes
            break;
        default:
            std::cout << "Invalid selection." << std::endl;
            return 1;
    }

    std::cout << "Total price: $" << price << std::endl;
}

If the user wants a large coffee. The price is 2 + 1 + 2 = $5. Each case adds an additional cost, which is all accumulated at the end of the switch.

We signal this intentional cascading via [[fallthrough]]. Thus, letting both the compiler and other programmers know this is intentional.

Unique case: No statements in the case

In this program, we want to validate multiple inputs. If they pass the validation they can fallthrough the other cases. We can do this without the compiler warning since the cases do not contain statements.

This is analogous to a hallway with numerous doors.

  • When the computer finds a match (e.g., c is 'e'), it “enters” that door.
  • Once it’s inside the hallway, it executes every line of code it sees until it hits a return or a break.
bool isVowel(char c)
{
    switch (c)
    {
    case 'a': // if c is 'a'
    case 'e': // or if c is 'e'
    case 'i': // or if c is 'i'
    case 'o': // or if c is 'o'
    case 'u': // or if c is 'u'
    case 'A': // or if c is 'A'
    case 'E': // or if c is 'E'
    case 'I': // or if c is 'I'
    case 'O': // or if c is 'O'
    case 'U': // or if c is 'U'
        return true;
    default:
        return false;
    }
}

Takeaway

[[fallthrough]] is a semantic tool used to convey intent.

The rule is simple: if a case has a body and no break/return, mark it [[fallthrough]] or fix the bug.