The fact that you can multiple-inherit from lambda types... C++ is so wild.
@yato33355 ай бұрын
Ikr, every time I watch these videos, I learn something new about C++
@minirop5 ай бұрын
since lambda desugar to a struct with operator() it's less "magical" (the syntax still looks like it thought).
@wowyomad5 ай бұрын
@@miniropso it's like functional interface in java
@Marlonbr15 ай бұрын
Thanks again Jason for explaining complex concepts in a very simple, understandable way !
@atheist8005 ай бұрын
Be mindful of the `const` qualifier when taking arguments by reference. I've had some surprises when I forgot to add `const` and overload resolution picked the default `auto &` case. This is especially subtle if this is a sub-element of a larger object. The `const` further up the ownership chain can be implicitly propagated down. It's easy to overlook this.
@jeremypewterschmidt6644 ай бұрын
This is pretty useful in a tight loop which you may wanna do some configurations before the loop starts with some boolean variables which will be evaluated in the loop. At this time, the runtime checking will create a branch which may break something like pipeline. But with `std::visit` and `std::variant` you van evaluate it in compile time.
@gregorssamsa5 ай бұрын
Note that std::visit requires return types to match for all covered types. This becomes a pain because you must leak abstractions down.
@Rojfos5 ай бұрын
Could somebody please explain?❤
@irrationalnumbers7715 ай бұрын
@@Rojfos The example in the video is trivial in that the return type (void) is independent of the input type. If however you have a visitor that has a return type that depends on the input type, then it gets more complicated as @gregossamsa eluded to
@goncalogomes50365 ай бұрын
in that case can't you just return a variant (I guess you need to be explicit about that in the return of the lambdas)
@irrationalnumbers7715 ай бұрын
@@goncalogomes5036 it all depends on your use case. For mine, the visitor would have some constexpr statements to determine how to handle the different variant types depending on the stored value type and modify an array. No return type was needed. The array would either be an array of variants or some templated container that didn't care what the underlying type was. I agree in principle though that returning a variant would also be possible from a lambda.
@vgglv3 ай бұрын
you just return another variant )
@ImmortalGuard235 ай бұрын
Very nice video! Thanks a lot! I would love to see part two of this where you go into details of implicit conversion pitfalls of the overload pattern and how to deal with these! (Andreas Fertig has a great blog post on safely visiting a std::variant)
@HaraldAchitz5 ай бұрын
Wonderful example of why we need pattern matching, all the workarounds we have to do because we do not have it are just painful
@Evan490BC5 ай бұрын
My thought exactly! Bring the bloody pattern matching into C++ already!
@MrAlbinopapa5 ай бұрын
@@Evan490BC Right? I'd be okay with something like: switch( my_variant ){ case: // do something with circle case: // do something with rectangle } I did try creating an interface for something like this. template struct Switch{ }; template struct Case{ }; Can't remember how I had it set up, it's been a few years. Basically, the program would have to compare each template Case parameter. Once a match is found, it executes the given Fn within the Case struct. It does clean up some of the inline code because it can be defined somewhere else, but has some added runtime cost.
@aniketbisht28235 ай бұрын
0:46 Jason, no doubt is a constexpr god.
@ohwow20745 ай бұрын
Damn this is crazy! I've never seen this sort of magic. Thanks for letting us know about this. Though I'm not a big fan of visitor.
@kaosce5 ай бұрын
Why is there no standard visitor class?
@josephlunderville31955 ай бұрын
Pattern matching, but obtuse and not directly supported by the compiler
@Henrik0x7F5 ай бұрын
Granted you are right. Though it still shows how powerful C++ is. In what other language would you be able to implement something like this in compile time and without language level support?
@yato33355 ай бұрын
This is crazy, I thought the current way of doing it was a chain of if constexpr (std::same_as)
@matt42hughes5 ай бұрын
these trivial examples are ok with only one line in each lambda, but i think i’d prefer reading the ‘if constexpr (std::same_as_v)’ version in a single large lambda. imo having multiple lambdas as args seems like it could be a little harder to follow/parse. I guess it’s down to personal preference.
@MrAlbinopapa5 ай бұрын
std::same_as is a concept, I think you mean std::is_same_v. I've played around with std::variant and have come up with several ways of dealing with variants. 1) Like inheritance. struct Circle{ float area()const; }; struct Rectangle{ float area()const; }; struct Shape{ float area()const{ return std::visit( []( auto const& shape_ ){ return shape_.area(); }, m_shape_var ); } std::variant m_shape_var; }; Usage: const auto area = shape.area(); 2) Conditional std::get_if or std::holds_alternative if( Circle* ptr = std::get_if( &shape.m_shape_var );ptr ) { // do things } else if( Rectangle* ptr = std::get_if( &shape.m_shape_var ){ do other things } if( std::holds_alternative( shape.m_shape_var ) ){ // do things } else if( std::holds_alternative( shape.m_shape_var) ){ // do other things } 3) Static dispatch; using both methods listed in this video 4) if constexpr block std::visit( [&]( auto const& var_ ){ using type = std::decay_t; if constexpr( std::is_same_v ){ // I do this so intellisense ( Visual Studio ) can show what's available Circle const& circle = var_; } else if constexpr( std::is_same_v ) { Rectangle const& rectangle = var_; } }, shape.m_shape_var ); My favorite use for std::variant is definitely in place of inheritance. This means I can create a single interface ( Shape ) and hide the implementation details in the pseudo derived classes. This also reduces the need for any of the other methods for dealing with specific types.
@yato33355 ай бұрын
@@MrAlbinopapa I clearly remember that concepts are "convertible" to booleans. And you can apply boolean && and || operators on them
@ToMikaa875 ай бұрын
@@MrAlbinopapa In case of 4), you can also simplify it using a lambda with an explicit template parameter list: std::visit( [&]( T const& var_ ){ if constexpr( std::is_same_v ){ // I do this so intellisense ( Visual Studio ) can show what's available Circle const& circle = var_; } else if constexpr( std::is_same_v ) { Rectangle const& rectangle = var_; } }, shape.m_shape_var );
@TsvetanDimitrov19765 ай бұрын
very neat implementation. i still want to have a generalized pattern matching if possible in the standard though.
@Megalcristo25 ай бұрын
I dont understand why this isnt the default, I can see that maybe you want to use an instance of a functor but most probably Inwould die before I would need that, instead this looks like the 99,99% use case for a visitor, but somehow we have to do this ourselves instead of a library doing it for us
@vetirtal11685 ай бұрын
But why would I want to use this over std::holds_alternative in an if/else-if/else statement?
@Traian-Enache5 ай бұрын
Because an if-else-if chain is an if-else-if chain, potentially evaluating the condition for all branches (and thus may branch N-1 times given N branches), which are not cheap. std::visit *may* be implemented using a table of function pointers, thus requiring only an array access into said table, and calling the obtained function pointer (which in turn casts the variant storage to a reference of the contained type and calling the apropriate overload of the provided function object). This results in a single “indirect jump/call”, which is faster than an arbitrary amount of branches.
@FruchteisMitErdbeer5 ай бұрын
Mainly because this does exhaustiveness checking. All variant types must be covered by one of the lambdas (or call operators if you're hand-writing a visitor), or you get a compile-time error. That way, when you add a type to the variant later, the compiler will helpfully point out all the places you need to adapt in your code.
@sadunozer22413 ай бұрын
❤
@MrAlbinopapa5 ай бұрын
Didn't we also get std::overload to do the exact same thing as the 'visitor' template struct you made?
@coder2k5 ай бұрын
As far as I know, there is no such thing as std::overload. But on cppreference there's a code example with a struct called "overloaded" that's pretty similar to what Jason did in this video. You can find it on the page about std::visit. It would be cool if that would be standardised, though!
@anon_y_mousse5 ай бұрын
This one feels like more of a hacky kludge from the committee to address a problem that would've been better addressed by adding on to switches. I'm sure if someone reads this they'll claim that changing switches would potentially break existing code somewhere somehow, but if the compiler is written correctly then it's simply a matter of context switching based on what types are being cased on and it shouldn't break anything if the compilers are written the correct way.
@cppweekly5 ай бұрын
I wouldn't look at this as a kludge from the committee - it's a thing that users came up with.
@anon_y_mousse5 ай бұрын
@@cppweekly It's in the standard though, which means they approved it.
@NotherPleb5 ай бұрын
This is not working for me on c++20 clang, anyone else got any idea?
@cppweekly5 ай бұрын
Depends on the version of clang, but it was slow implementing this feature.
@KX365 ай бұрын
I don't understand why you always shoehorn the Clion adverts in the middle of a sentence. Nobody else does that.
@enzosimone865 ай бұрын
kzbin.info/www/bejne/m6WUl52abb1kntE what can be also static starting from C++23?
@Raspredval13375 ай бұрын
the call operator ('operator( )') can now be static
You're moving the code for each case into separate functions, breaking the code flow. I no longer see what the print method does at a single glance, because you forced me to scour the source for all the implementations of print_value.
@norton77155 ай бұрын
@@vytah yes if you only write slideware then jason's version is better
@tbkih5 ай бұрын
@@norton7715 Even if you don't I would argue it's still nicer to use the clever fancy "visitor" multi-operator CTAD object with the lambdas, and _then_ delegate to print_int, print_float and print_string. Precisely for the reasons @vytah invokes: you get to see the dispatching table in one place.
@norton77155 ай бұрын
@@tbkih the code i posted does exactly what you are describing
@NosebergEatzbugsVonShekelstein5 ай бұрын
Screw all this noise, just check holds_alternative and act accordingly. Real life uses of std::variant aren't for integers and floats. A common use I see is for UI elements like buttons and windows and menus, where you have a sender and a receiver and the receiver needs to act according to it's sender. A generic lambda setup like this would just obfuscate and clutter the code.
@Danfranschwan25 ай бұрын
can this code style be anymore obscure and hard to read? ... drifting towards gimmicks don't you think? gimme real life problems that are better solved using this.
@enzosimone865 ай бұрын
You can have a method returning a std::variant, so the visitor MUST handle all possible returned types, some sort of pattern matching.
@gregorssamsa5 ай бұрын
Skill issue
@coc18415 ай бұрын
C++ has become an abominable language
@wizardy62675 ай бұрын
Became, like over 30yrs ago from Oct 1991
@WilhelmDrake5 ай бұрын
Then why are you watching the video?
@coc18415 ай бұрын
@@WilhelmDrake Coz' I'm a C++ dev, but I just don't like the language as much as before.