- Retain is and as contextual keywords from P2392.
- Combine binding and pattern in one syntax, like P1371 and Rust match.
The syntax for an inspect-clause is:
- constraint-seq binding?
constraint-seq is a sequence of constraints:
is type
is template
is constraint
is expr
is [ ]
- structured/designated patternis < >
- alternative pattern
An as-constraint is a test and a conversion
as type
- convert to type
There are four binding types:
-
ident
- an identifier binding -
[ binding1, constraint-seq binding?, ... ]
- structured binding -
[.name1: constraint-seq binding?, .name2: constraint-seq binding?, ... ]
- designated binding -
< constraint-seq binding? >
- alternative binding. -
Allow trailing if-guard as in rust, after the binding.
struct foo_t {
std::string s;
std::variant<int, double, float, const char*> v;
};
match(foo) {
// Designated pattern. Extract members s and v.
// s must be a string, bind to s.
// v must hold a double. bind to x.
[s: is std::string s, v: <double x>] => std::cout<< s<< ", "<< x<< "\n";
// test all alternatives of v, if convertible to double, bind to x.
// This implicitly generates a set of alternative successors, each with
// x declared with a different type.
[s: is std::string s, v: <as double x>] => std::cout<< s<< ", "<< x<< "\n";
// Bind to the active variant member of v. This causes a distinct successor
// for each alternative type.
// Prints "int", "double" or "float" depending on the active member.
// This is a generic visitor that P2392 lacks.
[_, <x>] => std::cout<< decltype(x).string<< "\n";
}
match(aggregate) {
// Destructure the first and last elements of an aggregate.
[is std::integral is 0..10, ..., is std::floating_point is 0..M_PI f] => ...;
}
match(tuple) {
// Check that the first and second elements are the same, and bind to x.
[x, is x] => std::cout<< x<< "\n";
// first element is greater than second element.
[x, y] if x > y => std::cout<< x<< ", "<< y<< "\n";
}
// P2391R1 1.1 example that will now work on all types (except any)
match(x) {
// Test if an int, then bind.
is int i => std::cout<< "int "<< i;
// Test if an integral. Don't bind here.
is std::integral => std::cout<< "non-int integral "<< x;
// Destructure 2-element tuple-like. Test if each element is int, then
// bind.
[is int a, is int b] => std::cout<< "2-int tuple "<< a<< " "<< b;
// Destructure 2-element tuple like. Test if element 0 is 0. We don't have
// to say "is 0" because the leading token isn't an identifier.
// Test element 1 with the even function, then bind y.
[is 0, is even y] => std::cout<< "point on y-axis and even y "<< y;
// Convertible to string. Different from the first clause, which tests if
// it's an int.
as std::string s => std::cout<< "string "<< s;
_ => std::cout<< "no matching value";
}
// P2392R1 3.2.2 Dereference example that will work on variants.
pair<variant<unique_ptr<Node>, Node*, double*, string>, int> v = ...;
match(v) {
// First element is either unique_ptr<Node> or Node*.
is [<*Node>] => ...
// First element is dereferencable (therefore double*)
[*_, _] => ...
// Fist element is anything (therefore string)
_ => ...
}
auto x = match(v1, v2) {
// Multiply the contents of variant 1 and variant 2.
// This generates a full NxM outer product. It's a 2D switch.
// We can only have single return type, so construct a common
// var type.
<x1>, <x2> => common_var_t(x1 * x2);
};