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