5 ms·
From the article: > it’s completely bonkers to expect the average user to build an overloaded callable object with recursive templates just to see if the thing
by maxlybbert 9y ago
From the article:
> it’s completely bonkers to expect the average user to build an overloaded callable object with recursive templates just to see if the thing they’re looking at holds an int or a string.
You don't have to: http://en.cppreference.com/w/cpp/utility/variant/holds_alternative http://en.cppreference.com/w/cpp/utility/variant/holds_alter... (and http://en.cppreference.com/w/cpp/utility/variant/get http://en.cppreference.com/w/cpp/utility/variant/get to access the value).
- clishem 9y agoShhh... You're disturbing Hacker News' anti-C++ circle jerk.
- dang 9y agoPlease don't post snarky, unsubstantive comments regardless of how wrong other people are about programming languages. https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html
- masklinn 9y agoAnd now you get neither exhaustive checking nor type-safe unwrapping, at this point is there really a point to variants? You may as well be using the old enum+union. (get_if at least nets you type-safe unwrapping similar to `if let` in Swift or Rust, though it returns a pointer rather than a reference)
- vinkelhake 9y agoYou either want exhaustive checking or "just [want] to see if the thing they’re looking at holds an int or a string". OPs comment was about the latter and holds_alternative and get accomplishes that. FWIW, std::get is type-safe in that you cannot specify a type outside of the variant types. It's safe at runtime in that it will throw std::bad_variant_access if the active object doesn't match the type. And because of this, you think we may as well use enum+union? Even if you only plan on manually type switching on a variant, std::variant saves you from a lot of boilerplate.
- deleted 9y ago[deleted]
- Deadolus 9y agoWhile I think it's just a honest overlook by the author (C++17 is just really new), I still think it's funny that a huge portion of the entire article and by extension the argument against variants is rendered moot by RTFM.
- feanaro 9y agoI think the author's goal was quite obviously to have strong sum types with compile-time safety and branch resolution. In other words, to make it impossible to use the value stored in the variant in a manner inconsistent with its type because doing otherwise is a compile-time error. The above parts of the standard library don't help in achieving this goal.
- atombender 9y agoNot the same as what the article is trying to accomplish, which is an exhaustive match (i.e., any unmatched value is guaranteed to fail at compile time). For example, consider a parser that matches on tokens. If you add a new token, the match should fail, because you want to guarantee, at compile-time, that every possible case is handled. This is one reason that the lack of sum types in Go is so painful, to the point that someone wrote a special library for it [1]. [1] https://github.com/BurntSushi/go-sumtype https://github.com/BurntSushi/go-sumtype
- maxlybbert 9y agoI'm familiar with the approach from other languages that the article tries to import into C++ before concluding that the designers should watch out for language envy (incidentally, Stroustrup wrote a paper on efficient type matches: http://stroustrup.com/OpenPatternMatching.pdf http://stroustrup.com/OpenPatternMatching.pdf ). The author picks a particular approach and then shows that the approach isn't satisfactory. But the author did not go back to try another approach. If the visitor approach is acceptable but has_alternative is not, get<> also accepts index values (as numeric template parameters, e.g., get<0>(v), get<1>(v), etc.) and variant has a method called index() to give a numeric value saying what the current type is. This is easy enough to use in a switch statement: std::variant<int, double, std::string> v; ... switch (v.index()) { case 0: std::printf("%d\n", std::get<0>(v)); break; case 1: std::printf("%f\n", std::get<1>(v)); break; case 2: std::puts(std::get<2>(v).c_str()); break; } In this case you can add a default: branch and throw an error for unhandled types, or you can hope that the compiler issues a warning about a missing case (index is constexpr, so it's possible for the compiler to know that you've missed something). It's not as good as a compile error, but it might be good enough.