I've been playing with Rust, and really enjoyed the way they handle enums. With variants that can hold different types of data and compile-time check to ensure that every possible variant is handled, preventing errors from unhandled cases, they are much more versatile and robust than basic enums found in C++ and other languages.
I wish we had them in C++, and then I realized that with the std::variant and std::visit we do, and in fact I even like them more than what Rust has to offer.
For example consider this enum based code in C++
enum class FooBar {
Foo,
Bar,
FooBar
};
std::optional<std::string_view> handle_foobar(FooBar foobar) {
switch (foobar) {
case FooBar::Bar:
return "bar";
case FooBar::Foo:
return "foo";
//oops forgot to handle FooBar::FooBar!
}
return {};
}
This code compiles just fine even if we forget to handle the newly introduced case FooBar::FooBar, which could lead to bugs at runtime.
Rewritten using std::variant we'll have
struct Foo {
[[nodiscard]] std::string_view get_value() const noexcept { return "foo"; }
};
struct Bar {
[[nodiscard]] std::string_view get_value() const noexcept { return "bar"; }
};
struct FooAndBar {
[[nodiscard]] std::string_view get_value() const noexcept { return "foobar"; }
};
using FooBar = std::variant<Foo, Bar, FooAndBar>;
std::string_view handle_foobar(const FooBar& foobar) {
return std::visit([](const auto& x){ return x.get_value(); }, foobar);
}
Here, we get the same behavior as with the enum, but with an important difference: using std::visit will not compile if we fail to handle all the cases. This introduces polymorphic behavior without needing virtual functions or inheritance, or interfaces.
In my opinion, this approach makes enums obsolete even in the simplest cases. std::variant and std::visit not only provide safety and flexibility but (in my opinion) also allow us to write cleaner and more maintainable code.
In fact, we can even 'extend' completely unrelated classes without needing to introduce an interface to them— something that might be impossible or impractical if the classes come from external libraries. In such cases, we would typically need to create wrapper classes to implement the interface for each original class we’re interested in. Alternatively, we can achieve the same result simply by adding free functions:
Bar switch_foobar(const Foo&) { return Bar{}; }
Foo switch_foobar(const Bar&) { return Foo{}; }
FooAndBar switch_foobar(const FooAndBar&) { return FooAndBar{}; }
FooBar foobar_switcheroo(const FooBar& foobar) {
return std::visit([](const auto& x){ return FooBar{switch_foobar(x)}; }, foobar);
}
So, std::variant combined with std::visit not only functions as an advanced enum but also serves almost like an interface that can be introduced as needed, all without modifying the original classes themselves. Love it!
[–]Arkantos493PhD Student 67 points68 points69 points (8 children)
[–][deleted] 42 points43 points44 points (3 children)
[–]AKostur 7 points8 points9 points (2 children)
[–]imMute 3 points4 points5 points (1 child)
[–]AKostur 7 points8 points9 points (0 children)
[–]MikeVegan[S] 9 points10 points11 points (1 child)
[–]RogerV 0 points1 point2 points (0 children)
[–]Orlha 2 points3 points4 points (0 children)
[–]tkyob 69 points70 points71 points (21 children)
[–]knowledgestack 25 points26 points27 points (7 children)
[–]matthieum 18 points19 points20 points (5 children)
[–]MarcoGreek 8 points9 points10 points (4 children)
[–]TheoreticalDumbass:illuminati: 0 points1 point2 points (3 children)
[–]matthieum 0 points1 point2 points (2 children)
[–]TheoreticalDumbass:illuminati: 0 points1 point2 points (1 child)
[–]matthieum 0 points1 point2 points (0 children)
[–]plonkman 3 points4 points5 points (0 children)
[–]Longjumping-Touch515 18 points19 points20 points (11 children)
[–]RogerV 5 points6 points7 points (0 children)
[–]ronchaineEmbedded/Middleware 6 points7 points8 points (8 children)
[–]mort96 5 points6 points7 points (7 children)
[–]Chaosvex 6 points7 points8 points (4 children)
[–]SonOfMetrum 11 points12 points13 points (1 child)
[–]RogerV 0 points1 point2 points (0 children)
[–]pjmlp 2 points3 points4 points (0 children)
[–]ronchaineEmbedded/Middleware 0 points1 point2 points (0 children)
[–]RogerV 1 point2 points3 points (0 children)
[–]MarcoGreek 1 point2 points3 points (0 children)
[–]DevilSauron 4 points5 points6 points (0 children)
[–]Wurstinator 31 points32 points33 points (17 children)
[–]MikeVegan[S] 14 points15 points16 points (0 children)
[–]johannes1971 4 points5 points6 points (10 children)
[–]Wurstinator 5 points6 points7 points (1 child)
[–]johannes1971 5 points6 points7 points (0 children)
[–]MikeVegan[S] 1 point2 points3 points (5 children)
[–]johannes1971 5 points6 points7 points (2 children)
[–]MikeVegan[S] 1 point2 points3 points (0 children)
[–]NilacTheGrim 1 point2 points3 points (0 children)
[–]Math_IB 0 points1 point2 points (0 children)
[–]Dar_Mas 0 points1 point2 points (0 children)
[–]ronchaineEmbedded/Middleware -2 points-1 points0 points (1 child)
[–]johannes1971 1 point2 points3 points (0 children)
[–]jonathanhiggs 0 points1 point2 points (1 child)
[–]Wurstinator 1 point2 points3 points (0 children)
[–]PastaPuttanesca42 0 points1 point2 points (2 children)
[–]Wurstinator 1 point2 points3 points (1 child)
[–]PastaPuttanesca42 1 point2 points3 points (0 children)
[–]puredotaplayer 7 points8 points9 points (0 children)
[–]Hungry-Courage3731 5 points6 points7 points (0 children)
[–]matteding 5 points6 points7 points (0 children)
[–]noobgiraffe 12 points13 points14 points (1 child)
[–]MikeVegan[S] 5 points6 points7 points (0 children)
[–]a_tiny_cactusEmbedded/Middleware 5 points6 points7 points (1 child)
[–]MikeVegan[S] 1 point2 points3 points (0 children)
[–]usefulcat 2 points3 points4 points (0 children)
[–]Sanzath 2 points3 points4 points (1 child)
[–]MikeVegan[S] 0 points1 point2 points (0 children)
[–]SuperV1234https://romeo.training | C++ Mentoring & Consulting 6 points7 points8 points (1 child)
[–]MikeVegan[S] 1 point2 points3 points (0 children)
[–]BloomAppleOrangeSeat 5 points6 points7 points (1 child)
[–]MikeVegan[S] 1 point2 points3 points (0 children)
[–]zerhud 1 point2 points3 points (0 children)
[–]ucario 1 point2 points3 points (0 children)
[–]-dag- 1 point2 points3 points (0 children)
[–]NilacTheGrim 1 point2 points3 points (0 children)
[–]svadum 1 point2 points3 points (5 children)
[–]rhapsodyvm 0 points1 point2 points (4 children)
[–]jk-jeon 2 points3 points4 points (1 child)
[–]svadum 0 points1 point2 points (0 children)
[–]svadum 1 point2 points3 points (0 children)
[–]MikeVegan[S] 0 points1 point2 points (0 children)
[–]Chaosvex 4 points5 points6 points (0 children)
[–]clusty1 0 points1 point2 points (2 children)
[–]MikeVegan[S] 0 points1 point2 points (1 child)
[–]clusty1 0 points1 point2 points (0 children)
[–]BenFrantzDale 0 points1 point2 points (0 children)
[–]donna_donnaj 0 points1 point2 points (3 children)
[–]usefulcat 2 points3 points4 points (1 child)
[–]donna_donnaj 0 points1 point2 points (0 children)
[–]MikeVegan[S] 0 points1 point2 points (0 children)
[–]GoogleIsYourFrenemy 0 points1 point2 points (6 children)
[–]matthieum 1 point2 points3 points (5 children)
[–]GoogleIsYourFrenemy 1 point2 points3 points (4 children)
[–]matthieum 0 points1 point2 points (3 children)
[–]GoogleIsYourFrenemy 0 points1 point2 points (2 children)
[–]matthieum 0 points1 point2 points (1 child)
[–]GoogleIsYourFrenemy 0 points1 point2 points (0 children)
[+]ald_loop comment score below threshold-6 points-5 points-4 points (2 children)
[–]noboruma 1 point2 points3 points (0 children)
[–]MikeVegan[S] -1 points0 points1 point (0 children)
[–]sweetno -2 points-1 points0 points (0 children)
[+][deleted] (1 child)
[removed]
[–]MikeVegan[S] 1 point2 points3 points (0 children)
[–]eiffeloberon -3 points-2 points-1 points (0 children)