C++ Pattern Matching

Powerful structural matching and value extraction (Future C++)

🎯 What is Pattern Matching?

Pattern Matching allows you to match values against patterns and extract data in one operation. It's a proposed C++ feature that makes code more expressive and safer than traditional switch statements.


// Future C++ pattern matching syntax (proposed)
#include <variant>
#include <iostream>

std::variant<int, std::string, double> value = 42;

// Pattern matching (proposed syntax)
inspect(value) {
    int i => std::cout << "Integer: " << i;
    std::string s => std::cout << "String: " << s;
    double d => std::cout << "Double: " << d;
}
                                    

Output:

Integer: 42

Pattern Matching Benefits

πŸ”

Structural

Match against data structure

Point{x, y} => process(x, y);
// Extract x, y from Point
πŸ›‘οΈ

Exhaustive

Compiler ensures all cases covered

// Compiler error if case missing
inspect(variant) { /* all cases */ }
πŸ“¦

Extracting

Bind values from matched patterns

Some(value) => use(value);
// Extract value from Some
🎨

Expressive

Clear intent and readable code

// More readable than if-else chains
inspect(data) { patterns... }

πŸ”Ή Current Alternative: std::visit

std::visit provides a way to operate on std::variant types, enabling type-safe visitation of alternatives. It works with a visitor callable that handles each possible type stored in the variant. For example, a visitor can print whether the variant holds an integer, string, or double. While powerful, it requires boilerplate and lacks the elegance of pattern matching. This mechanism is foundational for implementing polymorphic behavior without inheritance, commonly used in parsing and state management.

#include <variant>
#include <iostream>
#include <string>

using Value = std::variant<int, std::string, double>;

// Visitor for pattern-like matching
struct ValueVisitor {
    void operator()(int i) {
        std::cout << "Integer: " << i << std::endl;
    }
    
    void operator()(const std::string& s) {
        std::cout << "String: " << s << std::endl;
    }
    
    void operator()(double d) {
        std::cout << "Double: " << d << std::endl;
    }
};

int main() {
    Value v1 = 42;
    Value v2 = std::string("hello");
    Value v3 = 3.14;
    
    std::visit(ValueVisitor{}, v1);
    std::visit(ValueVisitor{}, v2);
    std::visit(ValueVisitor{}, v3);
    
    return 0;
}

Output:

Integer: 42

String: hello

Double: 3.14

πŸ”Ή Lambda-based Visiting

Generic lambdas simplify std::visit by allowing concise, inline visitor definitions without separate structs. Using auto parameters, a single lambda can handle multiple types stored in a std::variant. For example, std::visit([](auto&& arg) { std::cout << arg; }, myVariant); prints any variant value. This reduces boilerplate and improves readability, especially when the visitor logic is short. It’s a modern C++ technique that combines the power of variants with the convenience of lambdas for cleaner code.

#include <variant>
#include <iostream>
#include <string>

// Helper for overloaded lambdas
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

using Data = std::variant<int, std::string, bool>;

int main() {
    Data data = std::string("C++ Rocks!");
    
    // Pattern matching with lambdas
    std::visit(overloaded {
        [](int i) { 
            std::cout << "Got integer: " << i << std::endl; 
        },
        [](const std::string& s) { 
            std::cout << "Got string: " << s << std::endl; 
        },
        [](bool b) { 
            std::cout << "Got boolean: " << (b ? "true" : "false") << std::endl; 
        }
    }, data);
    
    return 0;
}

Output:

Got string: C++ Rocks!

πŸ”Ή Future Pattern Matching Syntax

Proposed pattern matching for C++ aims to simplify conditional code over types and values with a concise, expressive syntax. Inspired by functional languages, it would allow matching against structures like std::variant, tuples, and custom types using a match keyword. For example, matching a Shape could differentiate between Circle and Rectangle without verbose visitors. This feature, currently under discussion, promises to reduce boilerplate, improve readability, and make algebraic data types more accessible.

// PROPOSED FUTURE SYNTAX - Not yet in C++
#include <optional>
#include <variant>
#include <iostream>

struct Point { int x, y; };
using Shape = std::variant<Point, int>;  // Point or radius

void process_shape(Shape shape) {
    // Proposed pattern matching syntax
    inspect(shape) {
        Point{x, y} => {
            std::cout << "Point at (" << x << ", " << y << ")" << std::endl;
        }
        int radius => {
            std::cout << "Circle with radius " << radius << std::endl;
        }
    }
}

void process_optional(std::optional<int> opt) {
    inspect(opt) {
        std::nullopt => std::cout << "No value" << std::endl;
        int value => std::cout << "Value: " << value << std::endl;
    }
}

// This is conceptual - not yet available in C++

Conceptual Output:

Point at (10, 20)

Circle with radius 5

Value: 42

🧠 Test Your Knowledge

What C++ feature currently provides pattern-matching-like functionality?