Last active
October 8, 2024 00:42
-
-
Save petar-andrejic/d72a1219f927adb4c89e1041327eae13 to your computer and use it in GitHub Desktop.
Match statements in C++20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <print> | |
#include <variant> | |
template<class... Ts> | |
struct Pattern : Ts... { | |
using Ts::operator()...; | |
}; | |
template<class Variant> | |
struct Match { | |
const Variant &v; | |
explicit Match(const Variant &v) : v(v) {}; | |
template<class Pattern> | |
constexpr auto operator<<(const Pattern &p) const { | |
return std::visit(p, v); | |
} | |
}; | |
#define match(v) Match(v) << Pattern | |
// Concept alias: use as a constraint on the match arm to forbid implicit conversion! | |
template<typename U, typename V> | |
concept Case = std::same_as<U, V>; | |
int main() { | |
std::variant<long, int, float> v = 5; | |
int shouldBeFive = 0; | |
int shouldBeSix = 0; | |
shouldBeSix = match(v) { | |
[&](Case<int> auto val){ | |
std::println("An integer: {}", val); | |
shouldBeFive = val; | |
return 6; | |
}, | |
[](auto) { | |
std::println("Not implemented"); | |
return 0; | |
}, | |
}; | |
std::println("shouldBeFive is: {}", shouldBeFive); | |
std::println("shouldBeSix is: {}", shouldBeSix); | |
v = 3l; | |
match(v) { | |
[](Case<int> auto val) { | |
std::println("An integer: {}", val); | |
}, | |
[](Case<float> auto val) { | |
std::println("A float: {}", val); | |
}, | |
[](auto val) { | |
std::println("The default match (long): {}", val); | |
}, | |
}; | |
return 0; | |
} |
Because of constexpr everything inlines if possible, compiling down to jump tables: https://godbolt.org/z/aa38jG988 https://godbolt.org/z/qdanrqdr8 https://godbolt.org/z/EM7zK59YM
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Basically, std::visit sucks in syntax. The usual overload method makes visit a bit less painful, but I'd rather not wrap the block in parentheses. We also don't want implicit conversion in the overloads, but luckily in C++20 we have concepts and template lambdas! We can use
std::same_as
to constrain the lambda to only accept the case we considered without implicit conversion! We can then use unconstrainedauto
to match anything, as a default case. Exhaustive matching is already covered bystd::visit
at compile timeThe output: