Pattern matching proposes structured selection for C++
P1371R0 combines earlier proposals around inspect, with ordered matching and bindings that make structural cases visible.
A variant can make the alternatives in a data model explicit while leaving the code that examines them spread across visitors and nested conditions. P1371R0, dated January 21, proposes pattern matching for that gap. Its inspect construct combines selection with binding names to the parts of a matched value.
The paper is a collaboration between Sergei Murzin, Michael Park, David Sankel and Dan Sarginson. It merges two earlier directions after a joint presentation at the San Diego meeting. The resulting proposal addresses how C++ programs examine existing data structures. It does not announce a language feature that applications can already use in standard C++.
I am cautious about adding another way to express control flow in C++. Reading a substantial codebase already requires understanding several eras of the language. This proposal interests me where it removes the machinery needed to express a structural case and leaves the case itself visible.
Existing selection separates testing from binding
The proposal contrasts switch with if. A switch selects using an integral condition, while an if accepts a Boolean expression of arbitrary complexity. Testing the structure of a value often needs the flexibility of if, followed by declarations that extract the relevant components.
C++17 structured bindings already provide a concise way to name components. Michael Park's earlier P1260R0 develops pattern matching as a continuation of that idea. It asks whether the selection operation can perform structured inspection before establishing the bindings used by a branch.
A pair of coordinates illustrates the difference. A conventional implementation can bind both coordinates and then test whether each is zero. The proposed syntax puts each structural case at the branch that handles it. This example uses the January proposal's syntax and is not valid standard C++17:
inspect (point) {
[0, 0]: report_origin();
[0, y]: report_vertical(y);
[x, 0]: report_horizontal(x);
[x, y]: report_position(x, y);
}
The literal zero constrains a component. An identifier pattern binds a name referring to a component for use in the selected statement. The last branch accepts both components without constraining them to zero. That makes it a fallback for coordinates that reached neither axis case.
The code expresses a data partition directly. A reader can compare the patterns without reconstructing which earlier declaration corresponds to which component test. The advantage is most apparent when patterns become nested, because each condition and the names it introduces stay in one branch.
Ordering is a semantic choice
P1371R0 uses first-match semantics. It evaluates the condition and tries patterns in order. Once a pattern succeeds and its optional guard is true, execution passes to the corresponding statement.
Ordering is therefore observable in the coordinate example. The origin satisfies both axis descriptions as well as its own case. Placing the most general pattern first would select it before any specific description. The proposal does not search for a uniquely most specific match using overload resolution.
That choice makes the branches resemble an ordered if chain. It also makes local changes consequential. Inserting an earlier broad pattern can make a later specific one irrelevant even when both patterns remain well formed. A review of the conditions has to include their order.
The paper provides guards for relationships that a structural pattern alone cannot express. A guard can inspect bound names and decide whether the associated branch should run. A false guard lets matching continue. This avoids forcing every condition into a new pattern form.
That is useful for relations between components, such as comparing two extracted values. Structural inspection supplies the values, and the guard states the additional relation. Keeping those jobs separate can reduce the complexity of a pattern while retaining an explicit condition beside the branch.
Variants expose a different kind of selection
For variant-like values, the proposal supplies alternative patterns. These inspect the active alternative and then apply another pattern to the contained value. The paper discusses matching by type and by index, including variants whose alternatives repeat the same type.
Repeated types show why a variant's current type and its current index answer different questions. A variant with two integer alternatives can use the positions to represent different domain cases. A type-only test cannot distinguish those meanings. The pattern syntax therefore has to preserve access to the index distinction.
The proposal also covers polymorphic class hierarchies. That expands the scope beyond a closed list of variant alternatives. The set of possible dynamic types in such a hierarchy can be open, which changes what a compiler can know about completeness and about efficient dispatch.
I find that scope the demanding part of the design. One syntax can make different data models look similar, but their underlying costs and completeness properties still differ. A pattern over a tuple and a pattern involving dynamic type inspection need explanations appropriate to each operation.
Statement and expression forms have different obligations
P1371R0 proposes both statement and expression forms. A statement can perform an action in the selected branch. An expression must yield a result that can participate in surrounding code, so failure to match requires a defined response.
The basic model specifies that a statement with no matching pattern executes none of its branch statements. For the expression form, it proposes throwing std::no_match. This distinction gives an unmatched value different consequences depending on how the construct is used.
That is an important part of the interface to the construct. A programmer choosing an expression for a return value must account for the possibility of no branch yielding that value. Adding a catch-all pattern is a semantic choice about the remaining values, not merely a way to satisfy formatting.
An expression form can also keep the result selection close to the patterns that produce it. The cost is that the language must specify result typing and failure behavior in addition to branch selection. The two forms share structural ideas while serving different roles in surrounding code.
Extending structure without hiding the operation
The paper's custom matching and extraction mechanisms let user-defined types participate. That is necessary if inspect is to work with application abstractions instead of only a fixed collection of library types. It also means that a pattern can invoke operations supplied by those types.
The practical question is whether a reader can understand those operations from the abstraction's interface. Concise syntax helps only if it preserves a clear account of the work involved. Existing C++ templates can compile abstractions into direct operations, but understanding the abstraction still requires knowing the customization contract.
P1371R0 provides a concrete design against which those questions can be asked. Its ordered matching explains overlapping cases, and its bindings connect successful structural tests to the names used in a branch. For a data model expressed through tuples or variants, those details determine whether selection code communicates the model more directly than the visitors and conditions it would replace.