C++ Weekly - Ep 440 - Revisiting Visitors for std::visit

  Рет қаралды 13,502

C++ Weekly With Jason Turner

C++ Weekly With Jason Turner

Күн бұрын

Пікірлер: 62
@Minty_Meeo
@Minty_Meeo 5 ай бұрын
The fact that you can multiple-inherit from lambda types... C++ is so wild.
@yato3335
@yato3335 5 ай бұрын
Ikr, every time I watch these videos, I learn something new about C++
@minirop
@minirop 5 ай бұрын
since lambda desugar to a struct with operator() it's less "magical" (the syntax still looks like it thought).
@wowyomad
@wowyomad 5 ай бұрын
​@@miniropso it's like functional interface in java
@Marlonbr1
@Marlonbr1 5 ай бұрын
Thanks again Jason for explaining complex concepts in a very simple, understandable way !
@atheist800
@atheist800 5 ай бұрын
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.
@jeremypewterschmidt664
@jeremypewterschmidt664 4 ай бұрын
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.
@gregorssamsa
@gregorssamsa 5 ай бұрын
Note that std::visit requires return types to match for all covered types. This becomes a pain because you must leak abstractions down.
@Rojfos
@Rojfos 5 ай бұрын
Could somebody please explain?❤
@irrationalnumbers771
@irrationalnumbers771 5 ай бұрын
@@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
@goncalogomes5036
@goncalogomes5036 5 ай бұрын
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)
@irrationalnumbers771
@irrationalnumbers771 5 ай бұрын
@@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.
@vgglv
@vgglv 3 ай бұрын
you just return another variant )
@ImmortalGuard23
@ImmortalGuard23 5 ай бұрын
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)
@HaraldAchitz
@HaraldAchitz 5 ай бұрын
Wonderful example of why we need pattern matching, all the workarounds we have to do because we do not have it are just painful
@Evan490BC
@Evan490BC 5 ай бұрын
My thought exactly! Bring the bloody pattern matching into C++ already!
@MrAlbinopapa
@MrAlbinopapa 5 ай бұрын
@@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.
@aniketbisht2823
@aniketbisht2823 5 ай бұрын
0:46 Jason, no doubt is a constexpr god.
@ohwow2074
@ohwow2074 5 ай бұрын
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.
@kaosce
@kaosce 5 ай бұрын
Why is there no standard visitor class?
@josephlunderville3195
@josephlunderville3195 5 ай бұрын
Pattern matching, but obtuse and not directly supported by the compiler
@Henrik0x7F
@Henrik0x7F 5 ай бұрын
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?
@yato3335
@yato3335 5 ай бұрын
This is crazy, I thought the current way of doing it was a chain of if constexpr (std::same_as)
@matt42hughes
@matt42hughes 5 ай бұрын
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.
@MrAlbinopapa
@MrAlbinopapa 5 ай бұрын
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.
@yato3335
@yato3335 5 ай бұрын
@@MrAlbinopapa I clearly remember that concepts are "convertible" to booleans. And you can apply boolean && and || operators on them
@ToMikaa87
@ToMikaa87 5 ай бұрын
@@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 );
@TsvetanDimitrov1976
@TsvetanDimitrov1976 5 ай бұрын
very neat implementation. i still want to have a generalized pattern matching if possible in the standard though.
@Megalcristo2
@Megalcristo2 5 ай бұрын
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
@vetirtal1168
@vetirtal1168 5 ай бұрын
But why would I want to use this over std::holds_alternative in an if/else-if/else statement?
@Traian-Enache
@Traian-Enache 5 ай бұрын
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.
@FruchteisMitErdbeer
@FruchteisMitErdbeer 5 ай бұрын
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.
@sadunozer2241
@sadunozer2241 3 ай бұрын
@MrAlbinopapa
@MrAlbinopapa 5 ай бұрын
Didn't we also get std::overload to do the exact same thing as the 'visitor' template struct you made?
@coder2k
@coder2k 5 ай бұрын
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_mousse
@anon_y_mousse 5 ай бұрын
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.
@cppweekly
@cppweekly 5 ай бұрын
I wouldn't look at this as a kludge from the committee - it's a thing that users came up with.
@anon_y_mousse
@anon_y_mousse 5 ай бұрын
@@cppweekly It's in the standard though, which means they approved it.
@NotherPleb
@NotherPleb 5 ай бұрын
This is not working for me on c++20 clang, anyone else got any idea?
@cppweekly
@cppweekly 5 ай бұрын
Depends on the version of clang, but it was slow implementing this feature.
@KX36
@KX36 5 ай бұрын
I don't understand why you always shoehorn the Clion adverts in the middle of a sentence. Nobody else does that.
@enzosimone86
@enzosimone86 5 ай бұрын
kzbin.info/www/bejne/m6WUl52abb1kntE what can be also static starting from C++23?
@Raspredval1337
@Raspredval1337 5 ай бұрын
the call operator ('operator( )') can now be static
@DamianReloaded
@DamianReloaded 5 ай бұрын
int main() { auto print = [](auto y){std::visit([](auto x){ struct print { print (int i) { printf("%i ",i);} print (float f) { printf("%f ",f);} print (std::string_view sv) { printf("%.*s ", static_cast(sv.size()), sv.data()); } } print(x); }, y);}; std::variant var = 123; print(var); var = "Hello World!"; print(var); } aspirationally: int main() { std::visit( [](auto val){ std::cerr
@victotronics
@victotronics 5 ай бұрын
"Breda" has the stress on the 2nd syllable. "The Netherlands" has the definite article as part of the name.
@corpseopera
@corpseopera 3 ай бұрын
Why would he know about that
@acf2802
@acf2802 5 ай бұрын
Why is std::print a pile of crap?
@Raspredval1337
@Raspredval1337 5 ай бұрын
it's a new feature, it's gonna be good eventually
@norton7715
@norton7715 5 ай бұрын
void print_value(int i) { fmt::print("Int: {} ", i); } void print_value(float f) { fmt::print("Float: {} ", f); } void print_value(std::string_view sv) { fmt::print("SV: {} ", sv); } void print(value_t value) { std::visit([](auto value){ print_value(value); }, value); } int main() { const auto value = get_variant(); print(value); }
@vytah
@vytah 5 ай бұрын
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.
@norton7715
@norton7715 5 ай бұрын
@@vytah yes if you only write slideware then jason's version is better
@tbkih
@tbkih 5 ай бұрын
@@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.
@norton7715
@norton7715 5 ай бұрын
@@tbkih the code i posted does exactly what you are describing
@NosebergEatzbugsVonShekelstein
@NosebergEatzbugsVonShekelstein 5 ай бұрын
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.
@Danfranschwan2
@Danfranschwan2 5 ай бұрын
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.
@enzosimone86
@enzosimone86 5 ай бұрын
You can have a method returning a std::variant, so the visitor MUST handle all possible returned types, some sort of pattern matching.
@gregorssamsa
@gregorssamsa 5 ай бұрын
Skill issue
@coc1841
@coc1841 5 ай бұрын
C++ has become an abominable language
@wizardy6267
@wizardy6267 5 ай бұрын
Became, like over 30yrs ago from Oct 1991
@WilhelmDrake
@WilhelmDrake 5 ай бұрын
Then why are you watching the video?
@coc1841
@coc1841 5 ай бұрын
@@WilhelmDrake Coz' I'm a C++ dev, but I just don't like the language as much as before.
C++ Weekly - Ep 441 - What is Multiple Dispatch (and how does it apply to C++?)
10:59
C++ Weekly With Jason Turner
Рет қаралды 15 М.
C++ Weekly - Ep 404 - How (and Why) To Write Code That Avoids std::move
8:50
C++ Weekly With Jason Turner
Рет қаралды 34 М.
Сестра обхитрила!
00:17
Victoria Portfolio
Рет қаралды 958 М.
C++ Weekly - Ep 421 - You're Using optional, variant, pair, tuple, any, and expected Wrong!
10:34
Why is everyone LYING?
7:56
NeetCodeIO
Рет қаралды 383 М.
2 Years Of Learning C | Prime Reacts
22:24
ThePrimeTime
Рет қаралды 332 М.
Reacting to Controversial Opinions of Software Engineers
9:18
Fireship
Рет қаралды 2,2 МЛН
C++ Weekly - Ep 456 - RVO + Trivial Types = Faster Code
10:33
C++ Weekly With Jason Turner
Рет қаралды 16 М.
C++ Weekly - Ep 312 - Stop Using `constexpr` (And Use This Instead!)
18:24
C++ Weekly With Jason Turner
Рет қаралды 53 М.
Java for the Haters in 100 Seconds
2:22
Fireship
Рет қаралды 2,9 МЛН
Когда перепутал график девушек😁🐣
0:24
Alexey Merinov
Рет қаралды 3,1 МЛН
ЛАЙФХАК НА КУХНЕ ! 🧐🤦🏻‍♂️ #shorts #лайфхак
0:15
Крус Костилио
Рет қаралды 109 М.
НИКОГДА не иди на сделку с сестрой!
0:11
Даша Боровик
Рет қаралды 729 М.
The Million view clip on China's Tiktok P2428 #shorts #gochannel
0:15
Go Channel TV
Рет қаралды 29 МЛН