C++ Weekly - Ep 404 - How (and Why) To Write Code That Avoids std::move

แชร์
ฝัง
  • เผยแพร่เมื่อ 21 พ.ย. 2024

ความคิดเห็น • 208

  • @jef777
    @jef777 ปีที่แล้ว +250

    I really get the feeling we should hire you for onsight training

    • @AtomicAndi
      @AtomicAndi 11 หลายเดือนก่อน +5

      ... only then will he really answer the question!?

    • @PhilHord
      @PhilHord 11 หลายเดือนก่อน +13

      "on-site" is when you get training. "on sight" is when you shoot someone. Still not sure which one you meant, though.

    • @AtomicAndi
      @AtomicAndi 11 หลายเดือนก่อน +4

      @@PhilHord maybe Jason is the type of nerd who trains on sight?

    • @Evan490BC
      @Evan490BC 9 หลายเดือนก่อน +3

      @@AtomicAndi Yes, it's the educational approach called onslaught training.

    • @AtomicAndi
      @AtomicAndi 9 หลายเดือนก่อน +1

      @@Evan490BC OH, I never quite heard that correctly I guess. Now I cannot unhear.

  • @treyquattro
    @treyquattro 11 หลายเดือนก่อน +93

    you do realize that if you hire Jason to come onsite to teach you C++ that you have to std::move() him, and he will stay there until he goes out of scope.

    • @EmperorPear
      @EmperorPear 11 หลายเดือนก่อน +4

      brilliant one, mate🤣

  • @not_ever
    @not_ever ปีที่แล้ว +71

    Depending on your compller, you may be able to detect incorrect usage of std::move using compiler flags. For example gcc's Wpessimizing-move and Wredundant-move. For everything else I hear Jason Turner is available for onsite code review and training 😉

    • @GreenJalapenjo
      @GreenJalapenjo ปีที่แล้ว +14

      The thing is, the bad moves in this example aren't redundant or pessimizing moves, they're perfectly legitimate uses of move which no sane compiler would warn on. You could just do better by not creating the objects which need to be moved in the first place.

    • @iamgly
      @iamgly 11 หลายเดือนก่อน +3

      clang also have those flags

    • @not_ever
      @not_ever 11 หลายเดือนก่อน +2

      @@GreenJalapenjo So for everything else you can hire Jason?

    • @Febbe1
      @Febbe1 11 หลายเดือนก่อน +1

      @@GreenJalapenjo it's a legitimate use of a std::move, only because he made the mistake to define a move constructor and the destructor at all. That example is misleading and probably harmful for people who don't know how the compilation and optimizing actually works. Jason explicitly told the compiler, that there is no way to optimize the calls to the destructor and the move constructor at all. He basically told it to print something, when an object leaves lifetime or transfers ownership.

    • @GreenJalapenjo
      @GreenJalapenjo 11 หลายเดือนก่อน +9

      @@Febbe1 It's a dummy class which only exists to print what's going on, for illustration purposes. Imagine it was a std::vector or std::string instead. Moves would be more efficient than a copy, but not as efficient as using NRVO to construct the values in the right location in the first place.

  • @edubmf
    @edubmf ปีที่แล้ว +69

    This video is the strongest recession indicator I've seen.
    Best of luck Jason!

    • @treyquattro
      @treyquattro 11 หลายเดือนก่อน +5

      best of luck everyone I think

    • @tokyospliff
      @tokyospliff 11 หลายเดือนก่อน +1

      I'm selling half price baked beans, link in bio

    • @Evan490BC
      @Evan490BC 9 หลายเดือนก่อน +1

      @@tokyospliff I'm selling full-price half-baked beans. No customers yet...

  • @KennyMinigun
    @KennyMinigun ปีที่แล้ว +61

    The key takeaway: std::move will not solve the "extra copies" problem magically. It is about transferring resource ownership and NOT doing copy elision. Just use proper semantics...
    And yes, as a particular application of this principle is this particular use-case where std::move was used to attempt to resolve a problem it was not designed to resolve...

    • @vishaltripathy3620
      @vishaltripathy3620 ปีที่แล้ว +2

      c++ is copy by default. rust is move by default. rust is not faster

    • @antagonista8122
      @antagonista8122 ปีที่แล้ว +18

      @@vishaltripathy3620 And what was this take about, exactly?

    • @Febbe1
      @Febbe1 11 หลายเดือนก่อน +1

      @vishaltripathy3620 that's one of the most bullshit comments under this video. And probably that clickbait headline is the cause. But moving an object is always faster than coping an object. Both for rust and cpp. This video just teaches that a std::move can prevent optimisations like copy elision.

    • @KennyMinigun
      @KennyMinigun 11 หลายเดือนก่อน +2

      @@Febbe1 I shouldn't really respond to comments written in this manner but here you go, I'm responding to that bulllshit you just wrote.
      > But moving an object is always faster than coping an object
      No. It is not *always* waster. One simple example: if an object is fully allocated on the current stack, there is no way you can move that object "faster" than copying.

    • @Febbe1
      @Febbe1 11 หลายเดือนก่อน +2

      @@KennyMinigun Your example does not violate my statement, since an object, which recursively has automatic storage duration, is not "movable" and a universal quantification over an empty set is always true. You could argue, that one could implement a custom move constructor to trick the compiler, but that is negligible since this is a bad practice anyway.
      My problem with the original statement of @vishaltripathy3620 is, that he/she compared 2 completely distinct incomparable topics (rust vs c++) to justify a wrong statement (do not use std::move).

  • @roblayton3190
    @roblayton3190 11 หลายเดือนก่อน +15

    I agree with the folks that are telling you to chill with the hiring calls. It comes off desperate and condescending when you keep repeating…and…keep…repeating…it…slowly. Being condescending doesn’t make people want to hire or be around you.
    TH-cam is about making content for sponsor/ad/YTpremium revenue. If you don’t think the content is worth that exchange, just don’t make it. Don’t make it and then condescend people. Based on my experience, most people in this TH-cam community think information should be free and maybe apps/services should be paid. With courses and on-site training, you are gating information which is always going to be a hard sell.

    • @pmcgee003
      @pmcgee003 11 หลายเดือนก่อน +2

      Jason is 400 episodes in, just for cppw ... I don't think he likely needs advice on how TH-cam should be utilised, or his presentation style.

    • @roblayton3190
      @roblayton3190 11 หลายเดือนก่อน +10

      @@pmcgee003 So being 400+ episodes in gives you a free pass to be condescending to people? Who wants to pay to be talked to like that? It’s obvious he’s having trouble landing clients or he wouldn’t sound so bitter towards his audience.

    • @tiranobanderas5655
      @tiranobanderas5655 หลายเดือนก่อน

      @@roblayton3190 after 400+ episodes of free content, I'm pretty sure that he is 100% justified in wanting to actually make some money out of his content, no? he offers this same information with his on site training, that's what his job is all about, but instead, rather than selling it, he has decided to give part of his knowledge out for free. What is there to complain about? that he's asking for some breadcrumbs? holy shit.

  • @christopherhayden8442
    @christopherhayden8442 ปีที่แล้ว +37

    I think this video is an argument against "named temporaries", not `std::move`. I completely agree with avoiding named temporaries for exactly the reasons described, but there are many situations where `std::move` is useful and correct. The statement "`std::move` is a code smell" is so free of context that it is at best meaningless and at worst misleading.

    • @gtdcoder
      @gtdcoder 11 หลายเดือนก่อน +5

      Yes, I was going to say the same thing. The title of the video should probably be "Avoid using named temporaries" rather than avoid std::move.

    • @AtomicAndi
      @AtomicAndi 11 หลายเดือนก่อน +5

      exactly!
      But Jason probably sees many beginners using move to "optimize" temporaries.
      Better title: "How (and why) to avoid named temporaries and not use std::move with them and why you should hire me for onsite training if you ever see someone at your company doing it"

    • @Febbe1
      @Febbe1 11 หลายเดือนก่อน +2

      It is also an argument to avoid custom special member functions instead of defaulting / not declaring them at all. Actually, the video is misleading. Moving a truly movable object is at least as fast as copying it. The example in the video only works to demonstrate this wrong statement, **because** the compiler is allowed to copy elide. When that optimisation is disabled, the hole house of cards breaks. Then it's a decision which code path the compiler has to do. It will always print something.

    • @mocloun3709
      @mocloun3709 11 หลายเดือนก่อน +1

      Yep, exactly. Jason is mixing up two different problems. In his example, he just could construct the object directly with the value instead of a temporary object. For complex construction with several steps, it is always better to move this to a separate function.

    • @cppweekly
      @cppweekly  11 หลายเดือนก่อน +7

      Yes, that assessment is fair, about named temporaries. But I still say all uses of `std::move` should be examined, which puts them in the category of "code smell."

  • @hairnetart
    @hairnetart ปีที่แล้ว +15

    Ok, first 2 minutes of a 9 minute video is advertisement and then again after the first lines of code…

  • @abdullahcnar2227
    @abdullahcnar2227 11 หลายเดือนก่อน +3

    Dude, I watched half of the video but yet just couldn't skip over your advertisements

  • @Baile_an_Locha
    @Baile_an_Locha 11 หลายเดือนก่อน +5

    Regarding return value optimisation, in the case of Windows on x64, MS went further and it’s not just a compiler *optimisation*. It’s actually part of the ABI that any compliant compiler is *required* to implement. If the return value is more than 8 bytes, the caller must pass the address to sufficient storage in RCX.

    • @cppweekly
      @cppweekly  10 หลายเดือนก่อน +2

      It's actually not really an optimization with any ABI going back to about 1998

  • @kuhluhOG
    @kuhluhOG 10 หลายเดือนก่อน +6

    5:25 It is safe to call delete on a pointer with NULL or nullptr as a value.
    Interestingly enough, in the new C standard, calling free on NULL became UB.
    EDIT: Well, the C committee changed their mind (thank god). C23 is still not finished btw.

    • @shipweck6253
      @shipweck6253 7 หลายเดือนก่อน

      really? why would they do that?

    • @angelcaru
      @angelcaru 6 หลายเดือนก่อน +1

      free(NULL) is UB now???? nooooooooooooooo
      I mean, I don't think anyone is actually gonna change their behavior (especially because of dynamic linking and such, code written before C23 would still be affected), but that's SUCH A USEFUL FEATURE

    • @kuhluhOG
      @kuhluhOG 6 หลายเดือนก่อน

      Two the two people here, see my edit.

    • @angelcaru
      @angelcaru 6 หลายเดือนก่อน +1

      @@kuhluhOG > C23 is still not finished btw
      Well that's just a fucking fantastic name then , right? Good job, C committee!

  • @gabrielb.2886
    @gabrielb.2886 11 หลายเดือนก่อน +7

    The title should be "Don't use move when copy elision is available". And that I cannot agree more with: copy elision is a kind of compile time move and that's great.
    But code that implements strong ownership semantics has to use std::move extensively. Are you suggesting that ownership modeling should be avoided? I would bet that no, but how do you do that without std::move?

  • @TsvetanDimitrov1976
    @TsvetanDimitrov1976 ปีที่แล้ว +14

    The lambda solution is very nice, but in this specific example ading explicit Lifetime(int);(and removing the default ctor unless there's some compelling reason to have it) to the class definition would have the same effect and should be the idiomatic way to do it imo.

    • @not_ever
      @not_ever ปีที่แล้ว +8

      Hopefully that is obvious to everyone but I think it is hard to come up with trivial examples that fit nicely on the screen of a youtube video/slides to illustrate a point like this.

    • @TsvetanDimitrov1976
      @TsvetanDimitrov1976 ปีที่แล้ว +6

      @@not_everTrue, the best i can come up with is a class having a non-copiable data member, e.g. std::mutex

    • @nicholaskomsa1777
      @nicholaskomsa1777 6 หลายเดือนก่อน

      the lambda solution is not nice nor the solution. This is the solution:

      std::array arr = { LifetimeWriter{5}, {7} };

    • @dummyaccount1706
      @dummyaccount1706 หลายเดือนก่อน

      ​@@nicholaskomsa1777ok, but what if I need to initialize the array with a for-loop? Now I can't use initializer.

    • @nicholaskomsa1777
      @nicholaskomsa1777 11 วันที่ผ่านมา

      @@dummyaccount1706 Well like the video, you are going to have contrive this and that and why

  • @piotrbatko172
    @piotrbatko172 ปีที่แล้ว +21

    A little bit too many ads for me.

  • @syborg64
    @syborg64 10 หลายเดือนก่อน +2

    8 minutes to say implicit move-returns don't need std::move is a lot of time for what accounts to a compiler warning

  • @mytech6779
    @mytech6779 ปีที่แล้ว +5

    But I don't have a company and this is my training site. I live in here like The Lawnmower Man.

  • @sofasurfer3793
    @sofasurfer3793 ปีที่แล้ว +11

    Thank you for putting those pieces together! I knew about each of them separately, but never really grocked why you (and others) considered std::move a code smell. That was brilliant!

  • @BobruiskChessStar
    @BobruiskChessStar 6 หลายเดือนก่อน +1

    With this method you also don't have to worry about the moved-from stack variables l1 or l2 being, in a practical sense, invalid after the point of the move.

  • @coding_with_thomas
    @coding_with_thomas 11 หลายเดือนก่อน +1

    Great video, I had recently a similar issue with type erasure, where I then understood the emplace method of std::any. I guess this goes into the same direction.
    Cheers

  • @zamf
    @zamf 9 วันที่ผ่านมา

    The place where I see std::move as inevitable is when you're returning a transformed input argument from a function. By transformed I mean that you either return the input argument itself or an object constructed from this input argument or a member of the input argument.

  • @pippzzz
    @pippzzz ปีที่แล้ว +6

    Yep, we should definitely hire Jason for some internal training about what RVO is! Appeared in 1998, truly discovered in 2023 😄
    I'm usually a big fan of Jason content and talks, very enlightening, but this short one is...well...
    I'm actually surprised that someone with that pedigree doesn't build an explanation based on the differences between using RVO the right way on the one hand (as shown in that example), and avoid writing move constructors or assignment operators when it's pointless (trivial / non-movable types) on the other. If the point is to show that a lot of us are implementing move ctor/operator= when we shouldn't, the rationale behind it doesn't feel right, as RVO applies to any type, movable or non-movable.

    • @cppweekly
      @cppweekly  11 หลายเดือนก่อน

      Hmm... I didn't want *writing your own move operations* to come up AT ALL in this conversation. I am a very strong adherent to the Rule of 0, unless I really really need to.
      I truly wanted this episode to focus on "don't name things if you plan on passing them (via std::move) somewhere else"
      Use functions to generate values and let the compiler do the optimal thing.

  • @graham12345dd
    @graham12345dd 9 หลายเดือนก่อน +1

    Awesome as usual!

  • @fcolecumberri
    @fcolecumberri ปีที่แล้ว +7

    Happy "Page not found" Episode.

  • @systemdeadlock
    @systemdeadlock ปีที่แล้ว +4

    Nice - do you happen to have a video explaining where to use std::move?

    • @danielrhouck
      @danielrhouck ปีที่แล้ว

      -Whenever you have a range of values and you want to put them in another range, and donʼt care about the values in the old one, and there isnʼt a more specific alternative like splice available- wrong std::move

    • @cppweekly
      @cppweekly  11 หลายเดือนก่อน +2

      Only when "sinking" an object. Such as a constructor.
      But you have to make sure you use it correctly. Almost all use cases of `std::move` I see in the wild are incorrect.
      They either
      * don't use move when required to initialize a subobject
      or
      * Move something that is ultimately umoveable

    • @systemdeadlock
      @systemdeadlock 11 หลายเดือนก่อน +1

      Thanks Jason @cppweekly

  • @maliksmith8319
    @maliksmith8319 ปีที่แล้ว +6

    A programmer being condescending? I don't believe it!😲

  • @garyp.7501
    @garyp.7501 ปีที่แล้ว +1

    Thanks! That's a very clear explanation.

  • @jaybee9054
    @jaybee9054 11 หลายเดือนก่อน +1

    Good episode! And indeed - bills have to be paid :-)

  • @colinmaharaj
    @colinmaharaj 5 หลายเดือนก่อน

    I haven't looked at the video yet, but if i am creating a dynamic growable array, I may do a few news of a biggee array, moves and delete of rhe old.

  • @marcobonizzi3628
    @marcobonizzi3628 ปีที่แล้ว +1

    Very nice video , thanks Jason! A thorough understanding of object lifetime is really very, very relevant to write clean C++ code

  • @Flinsch77
    @Flinsch77 4 หลายเดือนก่อน

    I know it's just an example, but in the case shown I would rather just build an (explicit) constructor that I then call instead of this external helper function, like so: `std::array a{Lifetime(42), Lifetime(43)};`, or would I?

    • @daran0815
      @daran0815 3 หลายเดือนก่อน

      Yes, you probably would. Of course that would sidestep the point Jason tried to make.

  • @embracevoid
    @embracevoid ปีที่แล้ว +7

    I made quick benchmarks for both approaches on some compilers (with and without memory allocate/free in Lifetime object). std::move() slightly (~15%) better in both cases. As a rule of thumb I'd recommend benchmarking if performance matters when using lambdas/std::function/etc. Those usially have performance hit.

    • @BryceDixonDev
      @BryceDixonDev ปีที่แล้ว +3

      Would you mind sharing your benchmark?

    • @embracevoid
      @embracevoid ปีที่แล้ว

      ​@@BryceDixonDev Unfortunately I didn't expect that code would have any value for anybody and already deleted it. Actually it's a trivial task to do such benchmark using Google Benchmark library.

    • @oracleoftroy
      @oracleoftroy 11 หลายเดือนก่อน +2

      ​@@embracevoidGiven that your post seems oddly fixated on the lambda which was an implementation detail in Jason's example, I wonder if you introduced some pessimization in your benchmark, like using std::function when you didn't need it.
      From other videos, you'll see Jason recommend making lambda's static constexpr when not capturing, or if the goal is to disable ADL with a lambda, it would be instantiated globally, so there shouldnt be any overhead in a more production ready version of this example. And even constructing the lambda on the stack shouldn't be that bad as it has no captures.
      I'll have to try it myself, but your results seem suspicious.

    • @BryceDixonDev
      @BryceDixonDev 11 หลายเดือนก่อน +2

      @@oracleoftroy these were my thoughts, which is why I wanted to see the source. Just sharing results without context for how they are achieved is misleading at best.

    • @siteted2013
      @siteted2013 11 หลายเดือนก่อน

      Probably it could matter if destructor will do something bigger instead of just writing to console, but I'm not ready to try to check it with benchmark

  • @victotronics
    @victotronics ปีที่แล้ว +10

    It's not condescending if you actually know better, which you usually do :-)

  • @eamonconway375
    @eamonconway375 10 หลายเดือนก่อน

    Great video!
    I was interested in how this approach worked for different containers. I found that std::vector requires a default and move constructor as opposed to std::array. In a similar way, std::pair also requires a default and a move constructor but a struct containing two elements just requires two default constructors.
    It’s not obvious to me why there would be a difference in how they’re constructed?

    • @golvino
      @golvino 10 หลายเดือนก่อน

      My naive understanding is that, since an std::vector can be resized, this leads object to being moved in memory, which means that new objects are created at the new location and the older objects are moved in there (since C++ is not C# and objects cannot be moved in memory while preserving their identity). Arrays, on the other hand, have a fixed sized and objects never move. The requirements are thus very simple,

    • @cppweekly
      @cppweekly  10 หลายเดือนก่อน

      I need some more context for specific examples to discuss.

    • @eamonconway375
      @eamonconway375 10 หลายเดือนก่อน

      @@cppweekly
      This is the specific example I tried in godbolt. The output that I get says that the initialiser list for vector results in two default and two copy constructors. Using emplace_back (or push_back) results in two default and two move constructors. The default and move constructor is also the case for std::pair but not the simple struct.
      #include
      #include
      #include
      #include
      #include
      struct Lifetime {
      Lifetime() { std::puts("Lifetime() // default ctor"); }
      ~Lifetime() { std::puts("~Lifetime() // destructor"); }
      Lifetime(const Lifetime &) {
      std::puts("Lifetime(const Lifetime &) // copy ctor");
      }
      Lifetime(Lifetime &&) { std::puts("Lifetime(Lifetime &&) // move ctor"); }
      Lifetime &operator=(Lifetime &&) {
      std::puts("operator=(Lifetime &&) // move assign");
      return *this;
      }
      Lifetime &operator=(const Lifetime &) {
      std::puts("operator=(const Lifetime &) // copy assign");
      return *this;
      }
      int member_data;
      };
      template
      struct LifetimeStruct {
      T1 first;
      T2 second;
      };
      auto make_lifetime(const int value){
      Lifetime l;
      l.member_data = value;
      return l;
      };
      int main(){
      std::puts("Array");
      auto array_value = std::array{make_lifetime(12.0),make_lifetime(122.0)};
      std::puts("Vector");
      auto vector_value = std::vector{make_lifetime(12.0),make_lifetime(122.0)};
      std::puts("Vector emplace");
      std::vector vector_value_2;
      vector_value_2.reserve(2);
      vector_value_2.emplace_back(make_lifetime(12.0));
      vector_value_2.emplace_back(make_lifetime(122.0));
      std::puts("std::pair");
      auto pair_value = std::pair("first",make_lifetime(12.0));
      std::puts("std::make_pair");
      auto make_pair_value = std::make_pair("first",make_lifetime(12.0));
      std::puts("LifetimeStruct");
      auto struct_value = LifetimeStruct{"first", make_lifetime(12.0)};
      return 0;
      }

  • @MarcosCpp
    @MarcosCpp 3 หลายเดือนก่อน +1

    episode not found

  • @nicholaskomsa1777
    @nicholaskomsa1777 11 วันที่ผ่านมา

    You do not want to avoid move. If you want to shallow copy of an ephemeral object, you move it, simple as can be. If you want a deep copy an object, you do not move it. If for some reason you are worried about constructors or destructors being called, then you write those so they are not error prone and actually behave in a performant non contrived way. Or, you could rely on some magic sauce code that apparently is a life saver...

    • @cppweekly
      @cppweekly  9 วันที่ผ่านมา

      The point of this episode is that you should write your code in such a way as to avoid the need for move.

  • @RicardoCapurro
    @RicardoCapurro 11 หลายเดือนก่อน +2

    Good episode, but you can not always avoid std::move() ... so it would be good to explain the cases where you have to use std::move, and you can avoid using it.

  • @ryanjeffares8238
    @ryanjeffares8238 ปีที่แล้ว +3

    Say you had a situation like the following:
    class My class
    {
    MyClass(std::string s) : m_s{Std::move(s)} {}
    std::string m_s;
    };
    void foo() {
    std::string value = // get some value
    // insert some code that uses value as a reference
    MyClass m{std::move(value)};
    }
    Obviously, we only have one actual allocation for the string, but 3 (?) destructor calls. Is there a way this could be designed better to avoid all the destructor calls where you need to reference some data before it's moved into its final ownership?
    Unfortunately I don't have a company to hire you at ;) thanks for all you do

    • @TsvetanDimitrov1976
      @TsvetanDimitrov1976 ปีที่แล้ว +3

      Actually you have 2 allocations, because you're passing the value string by value. Unfortunately we can't really get rid of the calls to the dtors of moved from objects since there's no destructive move in c++, but they are basically noops.

    • @ryanjeffares8238
      @ryanjeffares8238 ปีที่แล้ว

      @@TsvetanDimitrov1976 is it 2 allocations? Yes the signature of the constructor takes by value but when it's actually called the string is moved, so I was under the impression that the parameter is move constructed and doesn't allocate

    • @youtubeviewer7077
      @youtubeviewer7077 ปีที่แล้ว +3

      @@TsvetanDimitrov1976 I think it's only 1 allocation of the innards of the std::string because the buffer is gutted from the one on foo's stack and moved into MyClass's constructor and then moved into MyClass's member variable in the initializer list. I don't have a compiler at hand, but I'm pretty sure that's right.

    • @Raspredval1337
      @Raspredval1337 ปีที่แล้ว

      it depends. If you passed an rvalue string then it would be only 1 allocation. However, if you had an existing string object and you pass it to the ctor of this class, then it would create a copy, thus you now have 2 strings.@@youtubeviewer7077
      the ideal way to deal with it is a lot of ugly glue code like that:
      class MyClass {
      public:
      MyClass( ) = default; // or MyClass( ) = delete; if you don't need a default ctor
      MyClass(const std::string& str) :
      m_str(str) { } // copy ctor
      MyClass(std::string&& str) noexcept : // noexcept is important for use in containers
      m_str(std::move(str) { } // move ctor
      template
      requires std::is_constructible_v
      MyClass(Args&&... args) noexcept(std::is_nothrow_constructible_v) :
      m_str(std::forward(args)...) { }
      /* /|\
      |
      this is an optional in-place constructor that will construct a new string using the arguments passed to the constructor. Note that since it has variadic arguments it WILL SHADOW regular copy/move ctors. But in this case it would act as a copy, or a move, or an in-place ctor depending on the passed arguments
      */
      private:
      std::string m_str;
      };
      Is is ugly af, but that's the most effective way to deal with the object construction. I hope for static reflection and code injection to be included in c++26 to abstract away a lot of glue code like that

    • @anon_y_mousse
      @anon_y_mousse 11 หลายเดือนก่อน

      @@youtubeviewer7077 You could always test with Compiler Explorer.

  • @tim37021
    @tim37021 6 หลายเดือนก่อน

    I know this behavior long time ago. So i always dare to return stack created object, since it is still fast enough.
    Handling rvalue reference sometimes hurt productivity.

    • @cppweekly
      @cppweekly  6 หลายเดือนก่อน

      Returning a local stack created object is literally the best possible option. th-cam.com/video/9mWWNYRHAIQ/w-d-xo.html

  • @christiandaley120
    @christiandaley120 11 หลายเดือนก่อน

    Jason, what are your thoughts on using std::move in constructors for initializing member variables? I like to do this because it gives the user of the class a choice as to whether they want to incur a potentially expensive copy or not. So, for example, instead of
    MyClass::MyClass(const std::string& str) : m_str{str} {}
    I always write
    MyClass::MyClass(std::string str) : m_str{std::move(str)} {}
    That way whenever the class is used, we can choose whether we want to copy some data into the class, or move it into the class.

    • @stephenhowe4107
      @stephenhowe4107 11 หลายเดือนก่อน

      Does not seem worthwhile. In the 2nd case, the copy constructor will always be called before using std::move. In the 1st case, the copy constructor is called in populating m_str. So the 1st case saves an extra move constructor.
      You cannot avoid the copy. And sometimes all have done is use an extra move constructor.

    • @christiandaley120
      @christiandaley120 11 หลายเดือนก่อน +3

      @@stephenhowe4107 It's not true that the copy constructor will always be called in the second case. If the caller passes in an r-value reference it will invoke the move constructor to create the str temporary.
      std::string myStr = "Hello, World!";
      MyClass myClass{std::move(myStr)};
      This results in two moves and zero copies. If I had implemented the MyClass constructor the first way, it would require a copy no matter what.

    • @cppweekly
      @cppweekly  11 หลายเดือนก่อน +1

      Yes, that is basically the one place to use move without question.

  • @Raspredval1337
    @Raspredval1337 ปีที่แล้ว +5

    but you still need std::move for implementing your own move ctors\assignment operators and glue code like that exists in every project that's somewhat nontrivial. C++ is known for solving nontrivial tasks, thus you still need know how and where to use std::move.

    • @PhilHord
      @PhilHord 11 หลายเดือนก่อน

      Agreed. The title of this video should be "How (and Why) To Write Slideware That Avoids std::move"

  • @MaitreBart
    @MaitreBart 6 หลายเดือนก่อน

    What is the _s: prefixing a string literal at 1min25sec, as the argument of puts(), at line 4?
    Is it c++?

    • @cppweekly
      @cppweekly  5 หลายเดือนก่อน

      That's the IDE inserting the name of the parameter to the function. It's not any code that I wrote.

  • @forgetfulfunctor1
    @forgetfulfunctor1 11 หลายเดือนก่อน +1

    I would hire you to come out to my company. If I didn't just get laid off

  • @adamkonrad
    @adamkonrad 11 หลายเดือนก่อน

    Hey Jason, C++ Weekly is great! Really hoping you don't read or at least ignore the stupid comments.

    • @cppweekly
      @cppweekly  11 หลายเดือนก่อน +1

      Note from Jason's assistant - I read and summarize the comments for him, so he does get to ignore a few of them without completely missing the important things. :)

  • @thomaswheeler4045
    @thomaswheeler4045 ปีที่แล้ว +2

    excellent video.

  • @IndramaniArora
    @IndramaniArora 5 หลายเดือนก่อน

    I tested the same with std::vector's initializer_list constructor and it makes a copy. Does anyone know why..??

    • @cppweekly
      @cppweekly  4 หลายเดือนก่อน

      Post a code sample and issue on the github for me to make a followup episode github.com/lefticus/cpp_weekly/issues

  • @djin81
    @djin81 11 หลายเดือนก่อน +3

    Randomly recommended this by youtube and two minutes in you've done NOTHING apart from plug your products in the most passive aggressive way possible. Will be adding to my ignore list.

    • @pmcgee003
      @pmcgee003 11 หลายเดือนก่อน +2

      Oh no ... he might be sweating bullets. Or not.

  • @Oaisus
    @Oaisus ปีที่แล้ว +2

    Is there no avoiding using std::move when passing ownership of a unique_ptr that already exists down into a function?

    • @if-constexpr
      @if-constexpr ปีที่แล้ว +4

      Personally, I consider that a very different case than what's been shown here. The initial intent of using std::move() here, was to avoid unnecessary copying, and in cases where the objects are expensive to copy, it still has a benefit.
      With unique_ptr, what is happening is transfer of ownership as it is not a copyable object, rather than avoiding unnecessary copy; the std::move() is in it's true spirit aiding in transfer of ownership of a resource from one object to another and keeping the semantic of that class intact. So, I wouldn't see it as a code smell or a negative.

    • @oracleoftroy
      @oracleoftroy 11 หลายเดือนก่อน

      I really do hope that you are moving the unique_ptr because you really are transferring ownership. It is perfectly fine to pass the raw pointer from unique_ptr::get(), or a reference to the value (after a proper nullptr check) to a function as a parameter.
      Just want to make that clear to anyone reading because sometimes people get confused and think that if they are using smart pointers, they have to pass the smart pointer object itself everywhere.

  • @kimhyunpil
    @kimhyunpil 9 หลายเดือนก่อน

    amazing!

  • @fishhead314
    @fishhead314 ปีที่แล้ว +1

    Short and sweet.
    Good content.
    +1

  • @ta1091
    @ta1091 วันที่ผ่านมา

    ads before the start of the video, ads in the video, ads nested inside the video, ads after the video

  • @samuelgunter
    @samuelgunter 9 หลายเดือนก่อน +1

    as someone without a company, I think I should found a company just so that I can bring you in for onsite training

  • @TheMR-777
    @TheMR-777 ปีที่แล้ว +4

    404 - Comment Not Found

    • @md2perpe
      @md2perpe 6 หลายเดือนก่อน +1

      Or "Episode not found"?

  • @alienpioneer
    @alienpioneer 9 หลายเดือนก่อน +1

    I love your videos Jason, but this isn't a case where you shouldn't use move, but an example where you don't need at all move. This has nothing to do with the cases where move should be used ! And you don't need a lambda at all, all you need is a proper constructor or a constructor delegate.

  • @nicholaskomsa1777
    @nicholaskomsa1777 6 หลายเดือนก่อน

    Hello, fellow C++ enthusiast, please learn to do this instead:
    struct LifetimeWriter {
    int value{ 0 };
    LifetimeWriter( int value)
    :value(value)
    {
    std::cout

  • @杨万里-i4k
    @杨万里-i4k 11 หลายเดือนก่อน

    Is that RVO optimization?

    • @cppweekly
      @cppweekly  11 หลายเดือนก่อน

      It's partially RVO, but I don't like the name "RVO" (return value optimization) because it implies that compiler has actually "optimized" something. 99% of the time it falls out from how calling conventions work.

  • @CramBL
    @CramBL ปีที่แล้ว +2

    Condescending? It just made me think. Thanks for the episode!

  • @wigi426
    @wigi426 ปีที่แล้ว +4

    so it's less about not calling std::move and more about not using the move constructor/assignment for any non trivially destructible object?

    • @Omnifarious0
      @Omnifarious0 ปีที่แล้ว +1

      If you have to explicitly call ::std::move, you're probably doing something sub-optimally.

    • @wigi426
      @wigi426 ปีที่แล้ว +1

      @@Omnifarious0 I agree and i tend to find that well thought out code won't call for the use anyway. But as far as the point of the video goes, Jason is less talking about the actual call to move(or the cast to r-value&) and more about the very use of move constructors on objects with involved destruction

    • @Omnifarious0
      @Omnifarious0 ปีที่แล้ว

      @@wigi426 - Yes, I suppose so. This kind of thing is why things like emplace_back even exist.

    • @if-constexpr
      @if-constexpr ปีที่แล้ว +6

      @@Omnifarious0 I'd say that in cases of transferring ownership, std::move() makes total sense. Semantically, I always saw std::move() as a way of ownership transfer, more than "here's this function for optimization" as many programmers seem to see it for.

    • @cppweekly
      @cppweekly  11 หลายเดือนก่อน

      Those are effectively the same thing, but the point being that you should arrange your code so that move is not a question. Don't name things you plan on giving to another function.

  • @Omnifarious0
    @Omnifarious0 ปีที่แล้ว +2

    Telling people they can get more information by hiring you is good advertising. It's not condescending.

  • @ItsBaffledd
    @ItsBaffledd 11 หลายเดือนก่อน +1

    You’ve essentially just forwarded their construction, like if you used emplace({42}). Wrapping it and a lambda doesn’t change anything here? Am I missing something

    • @cppweekly
      @cppweekly  11 หลายเดือนก่อน +1

      The key is to delay construction until you have enough information that you can construct the object with a meaningful value. This is also why "const" is a good default - you must wait to initialize an object until you can initialize it with a meaningful value.
      Many of the same code quality improvements result.

    • @ItsBaffledd
      @ItsBaffledd 11 หลายเดือนก่อน

      Yeah okay, I can see how this would be more useful if the class didn't expose certain things in a constructor and you would otherwise need the object instance to start setting values, I think I was taking your example a little too literal and wasn't seeing the bigger picture.@@cppweekly

  • @awesomedavid2012
    @awesomedavid2012 2 หลายเดือนก่อน

    I always avoid std::move because I use languages other than C++

  • @jaorlowski
    @jaorlowski 9 หลายเดือนก่อน

    That means, that if you used the destructor for book keeping (some performance or correctness metric for instance) and assumed that move really transferred ownership, you'd be in for a surprise..? Would it be complicated to explain what happens from a memory layout perspective there? Destructors being caled at the end of a scope or stack frame, would there then be a relocation? Or maybe if you use a local variable in a local thread and then want to write selcted elements into a data structure owned by the main thread. Could you then std::move? Does it make a difference if it came from a data structure like a vector?

    • @cppweekly
      @cppweekly  9 หลายเดือนก่อน +1

      Move does exactly what the implementer asked it to do.
      But on a different note - if you're using your destructor for bookkeeping, like performance tracking, you are DRASTICALLY affecting the performance of your system. If your object could otherwise be trivially destructable, then it becomes unknowable how many of the objects were destroyed.

  • @cmdlp4178
    @cmdlp4178 ปีที่แล้ว +1

    Accidentally moved from unique_ptr variable, 404 not found

  • @maxaafbackname5562
    @maxaafbackname5562 11 หลายเดือนก่อน

    What is the difference between using the lambda and an explicit construtor using an integer?

    • @cppweekly
      @cppweekly  11 หลายเดือนก่อน +1

      A named function (or constructor) can be used in place of a lambda with the same effect.

  • @iamgly
    @iamgly 11 หลายเดือนก่อน +2

    The "problem" in C++ is that move is not destructive, meaning you still can access to the variable after a move (which, in fact, is UB). And that's because compiler doesn't have borrow checker (like Rust have for example), so the choice of the move design is coherent.
    If the C++ had the destructive move design, the destructor would not be called on an instance that has been moved, and we won't avoid using move for any kind of objects.
    Now, knowing that, using move is not a big deal. We just have to remember to nullify the "other" object during a move to not end up with a bad situation when the destructor is called. Sure the optimisation of building the instance in the exact spot is better is it calls exactly 1 ctor and 1 dtor, but move can still be useful in a lot of case ;)

    • @oracleoftroy
      @oracleoftroy 11 หลายเดือนก่อน +3

      It is most definitely *not* undefined behavior to access a moved from variable. It is unspecified but valid. The standard can't specify exactly what a moved from type must do because it doesn't know your codebase, but it does specify what the standard types do. E.g. a moved from unique_ptr is like it was set to nullptr, or a moved from vector is like it is empty. The absolute minimum is that it must be possible to destruct the object.
      Any undefined behavior in the code isn't from the move, it's from treating the moved from object as if it still had its data without doing the proper bounds checks.

    • @iamgly
      @iamgly 11 หลายเดือนก่อน

      ​@@oracleoftroy You are right but it's not garanteed that the moved value stays in a valid/reusable state. For example, it's fine to just set a boolean to indicate to the destructor if a value is moved or not (and by fine, I just mean it works, not that we should do it !). So it is potential undefined behavior not by the compilers implementation but by how libraries work around this.

    • @oracleoftroy
      @oracleoftroy 11 หลายเดือนก่อน +1

      @@iamgly I do find that designing movable types tends to push for some sort of 'zero' state, even if normally I would prefer to discourage a 'default construct then assign' pattern. But I can't think of any sort of reasonable move constructor/assignment operator implementation that wouldn't allow at minimum reassignment after move. You need it for std::swap to work.
      A bool flag can be fine if there isn't a more natural zero state, but you usually want move because you hold a pointer and are avoiding a deep copy, so usually there is something you could null instead. But move only types that aren't pointers can be great for some things (sockets, opengl wrappers), and not all of them have a natural default state value.
      A swap based implementation can also be fine and avoids the extra bool member. As the moved from value is typically expiring soon anyway, there isn't a problem keeping the old values alive a bit longer and letting the destruction do the real cleanup. Such an implementation should obviously have no undefined behavior unless something else is seriously borked.
      In my experience, it really isn't hard writing a move constructor/assignment operator that behaves properly without the gotchas you are imagining. It takes a bit of getting used to, but really isn't too different from any other resource management concern than normal copying and RAII deal with. Pro Rust arguments would be stronger if they showed more awareness and less fearmongering about the actual state of C++.

    • @anon_y_mousse
      @anon_y_mousse 11 หลายเดือนก่อน +1

      @@oracleoftroy Unfortunately, a Rustacean's arguments can never be stronger because if they actually knew C++ instead of spreading FUD about the language then they wouldn't bother using Rust as idiomatic C++ is equally as safe as Rust, faster to compile and execute, and allows for doing "unsafe" things without a stupid keyword.

    • @oracleoftroy
      @oracleoftroy 11 หลายเดือนก่อน +1

      @@anon_y_mousse I don't know if I would go quite that far. I think there is real value in the borrow checker. I'm not sure it is quite as high as Rust claims, but it isn't zero and I look forward to C++ borrowing the best parts of the borrow checker on future standards and implementations.
      But that said, I rarely, though not never, have the sort of issues Rust seeks to protect against. The last time I ran into it, I was doing low level code for an emulator, the sort of code I think would need to be marked unsafe in Rust anyway. I was also writing the sort of code I knew wouldn't have lifetime issues, but I've tried to do in Rust and couldn't appease the borrow checker for the life of me. Often the examples of code Rust rejects but C++ accepts are the sort that would never pass review, but to Rust's credit, Rust finds it and C++ doesn't, and that has value.
      I think for truly secure code, you still want static analyzers, sanitizers, fuzzers, and all the other sorts of tooling we have in C++ to help ensure correctness, even in Rust. A perfectly accurate borrow checker is probably a halting problem difficult undertaking, so Rust favors false positives over missing real issues. I'm not sure Rust's overly strict borrow checker is the right answer. Maybe a relaxed version that never has false positives but needs supplemental sanitizers, etc. to detect other issues would be better, especially since we probably still need them anyway.

  • @vasilivoloshin
    @vasilivoloshin ปีที่แล้ว

    When I was writing in Qt I noticed that they have an object.swap(other object) functions in their framework. Since then I always thought of move == swap and it kind-of behaves like this. I think maybe name swap would be better.

    • @WiZaRdNoOb101
      @WiZaRdNoOb101 11 หลายเดือนก่อน +2

      iirc, std::move takes the object you pass it and returns an r value allowing you to use move semantics on it. I don't think you're actually swapping anything.

  • @Sluggernaut
    @Sluggernaut 5 หลายเดือนก่อน

    Ok, wait wth. No. This is why people use move. So the destructor is NOT called and they can move the object. They DONT want to construct a new one. The destructor is called when the object falls out of scope. So, yes, it's called 4 times so there is this reference count in action (the mechanism of which I have no idea) but this seems like it's doing exactly what we want. Right?

    • @cppweekly
      @cppweekly  5 หลายเดือนก่อน

      I don't understand your reasoning. "moving" requires that there is more than 1 object in existence. It's *always* better to have fewer objects when you can. Fewer constructors, fewer destructors, less instruction cache pressure, less register allocation pressure... This is *always* a performance win.
      The point of the video is how to get that performance win by avoiding both copies and moves and relying on copy and move elision, and directly constructing values in place.

  • @itsreality3646
    @itsreality3646 ปีที่แล้ว

    For your own health, lets hope Bjarne is not seeing this. x)

    • @cppweekly
      @cppweekly  11 หลายเดือนก่อน

      Bjarne tends to agree with my Best Practices. He even regularly recommends my Commodore 64 talk to people. (I have people come up to me at conferences and tell me this.) I'm not worried about Bjarne seeing this episode.

  • @bez_64
    @bez_64 3 หลายเดือนก่อน

    The paranoia of this man to sacrifice code complexity and readability to avoid calling delete on null pointers...
    This paints a terrible example of writing code in general. There is nothing wrong with std::move.
    And what if I want to get multiple types of data and do some operations with them before pushing them in a container? Is it really better to make a lambda for every data type?

    • @cppweekly
      @cppweekly  2 หลายเดือนก่อน

      You appear to have missed the point of this episode - if you structure your code in such a way that you don't need or want std::move, then you will get more efficient, composable, and cleaner code in general.

  • @Lecluyse2000
    @Lecluyse2000 8 หลายเดือนก่อน

    Can anyone tell me why he uses puts instead of cout

    • @cppweekly
      @cppweekly  8 หลายเดือนก่อน +2

      Fast compile times, smaller binaries, does exactly what I want, and makes for more readable assembly output.
      th-cam.com/video/VZTVmKOXLVU/w-d-xo.html

    • @Lecluyse2000
      @Lecluyse2000 7 หลายเดือนก่อน

      Thank you very much@@cppweekly

  • @thestarinthesky_
    @thestarinthesky_ ปีที่แล้ว

    I use std::cout in Compiler Explorer and just out of curiosity wondering why you don't use std::cout instead of std::puts.

    • @hampus23
      @hampus23 ปีที่แล้ว +2

      More assembly code for similar result

    • @Raspredval1337
      @Raspredval1337 ปีที่แล้ว +1

      you should use std::format\std::print(std::println) instead (or fmt library if you don't have c++23 support yet). Iostreams have a hidden cost of synchronizing with the c stdio library; plus library has better formatting syntax and is type safe

    • @cppweekly
      @cppweekly  11 หลายเดือนก่อน

      @@Raspredval1337 I'm not worried about runtime cost in this case - I'm just trying to generate cleaner assembly that's easier to read. `puts` is effectively "free" when it comes to the assembly output.

  • @MikelLuri
    @MikelLuri ปีที่แล้ว +1

    I'd really love to have you in for some training, but apparently it is "too expensive"... 😢

  • @Sluggernaut
    @Sluggernaut 5 หลายเดือนก่อน

    Alright, call me a d-bag, because I am but here is my clapback: Does anyone hire you (OR ANYONE) for on-site training? I've worked at AMD and a company of 5 people and never had on-site training except for a specific product we bought (Jenkins). Employers want to test senior applicants on this sort of material in the interview and MAYBE have them teach the junior and mid devs. Or just have them pick it up through code comments and code reviews, not through specifically scheduling training between the two.
    After that, I didn't fully follow the video. Your example, just before the lambda was making 4 instances of the object, explicitly. Then, you made the lambda and had the array members filled out with that. As you know, this is quite different from the code just before that. I see you're sort of showing that you can initialize in place and add to the array all at once and you did explicitly state how the compiler is taking care of single moves (without using implicit moves or anything since yours are explicit) but Im otherwise not connecting these behaviors before and after the refactor. They're very different to me.
    Ok after rubber ducking it a bit, writing the above statement, I think I'm following what you're exemplifying better but still am lacking to come to the same conclusion you are.
    Either way, I appreciate the video. I understand your frustration a bit since your goal is to get hired for on site training but I feel the responses you got that brought you to make this video could be easily foreseen.
    Edit: Ok after I walked away and thought a moment, I realized (afaik) people think that the std::move somehow totally would save a copy/construction operation and you were showing it does not. I guess the reason I didn't get this is because I am aware that the code would behave just as you showed it would.

    • @cppweekly
      @cppweekly  5 หลายเดือนก่อน

      Yes, many companies hire me and many other trainers for on-site training. This is actually a very normal thing. I suggest you look around for different companies that care about investing into the education of their developers.

  • @the0neskater
    @the0neskater ปีที่แล้ว +4

    You need to chill on the hiring calls, it's a bit desperate sounding IMO honestly sorry. Interesting thought though that I hadn't considered, using RVO to avoid any copies or moves entirely.

  • @veetaha
    @veetaha ปีที่แล้ว +1

    Waiting for your Rust course!

  • @onehotseat
    @onehotseat ปีที่แล้ว +1

    Shill moar!

  • @NotherPleb
    @NotherPleb 11 หลายเดือนก่อน

    If C++ didn't allow the use of moved values this wouldn't be a question. But misleading, as the example is just for a "factory" pattern, there are plenty of examples where you should move.

  • @oschonrock
    @oschonrock ปีที่แล้ว

    Bit of a perennial this one. But still always a crowd pleaser.

  • @adrianojordao4634
    @adrianojordao4634 4 หลายเดือนก่อน

    I will not contract you for my company! I m no dummy.

  • @VioletGiraffe
    @VioletGiraffe ปีที่แล้ว +2

    Jetbrains stuff is awful, no matter how much money they throw into marketing.

    • @bloodgain
      @bloodgain ปีที่แล้ว

      Compared to what? Serious question.
      - Visual Studio is probably still the most functional C++ IDE, IMHO, but it's still not fully cross-platform, and much of my work is done on Linux. However, JetBrains has its ReSharper products that enhance it even further.
      - Eclipse is still pretty decent for Java, but not great at anything else, and its design is heavily weighed down by legacy.
      - I can't think of any IDE that even approaches PyCharm's support for Python.
      - VSCode is a good programmer-oriented editor, but requires a lot of configuration as a DIY IDE, and I already have Vim/NeoVim as an option. However, VSCode's Vim/NeoVim plugins bring all that power to VSCode, too, which is nice.
      JetBrains products are very competitive with all of these IDE options, and it has a very good modern foundation in its IDEA platform that no other platform is competing with. VS Code is the closest anyone has come, but it lacks the "batteries included" aspect of JetBrains' language/task-specific IDEs -- but it's free, which is a fair tradeoff.

    • @_supervolcano
      @_supervolcano ปีที่แล้ว +2

      CLion is a godsend.

    • @sergeykolesnik1171
      @sergeykolesnik1171 11 หลายเดือนก่อน

      @@_supervolcano sent by a Jewish-like god as a punishment for working with projects that are not even that large.

    • @adamkonrad
      @adamkonrad 11 หลายเดือนก่อน

      Nope, you're wrong. Go back to your VS Code.

    • @VioletGiraffe
      @VioletGiraffe 11 หลายเดือนก่อน

      @@adamkonrad, I prefer Visual Studio Proper and Qt Creator, but VS Code also works extremely well, for an Electron app it's amazing. Jetbrains IDEs, on the other hand, can load even a 32-core CPU to 80% with bullshit background tasks that run for minutes.

  • @lostphysicist
    @lostphysicist ปีที่แล้ว

    Interesting... do you guys think Jason is ready to hate C++ and love Rust? 🙂

    • @stephenhowe4107
      @stephenhowe4107 ปีที่แล้ว +4

      No, not at all

    • @TsvetanDimitrov1976
      @TsvetanDimitrov1976 ปีที่แล้ว +1

      Rust has its own problems, and tbh the borrow checker doesn't really offer anything to an expert likie Jason. Still I wish c++ had destructive move.

    • @lostphysicist
      @lostphysicist ปีที่แล้ว

      @@TsvetanDimitrov1976 "has its problems"... lol. The arrogance...
      You're not smart enough to handle a million states in your head. Pull your head out of your butt and smell the reality. Many people who are masters in C and C++ have been doing memory bugs, including whoever you consider your mentor. Guaranteed. When you say the borrow checker offers "nothing" in a video that shows how C++ move is broken... well, you have a few more lessons to learn in being humble, Mr. Expert.
      And btw, I did C++ for 15 years. But I'm not arrogant enough to think that my brain is better than compilers to track tons of states.

    • @az-kalaak6215
      @az-kalaak6215 ปีที่แล้ว +2

      ​@@TsvetanDimitrov1976aren't destructive move less optimized than simple ownership transfer? since you have to flag in some way that the object is now in "destroyed state"

    • @not_ever
      @not_ever 11 หลายเดือนก่อน +2

      At this point he might just be ready to hate people who leave Rust comments on C++ videos every week like I am. I do like Rust though.

  • @reneb86
    @reneb86 6 หลายเดือนก่อน

    Oh man. I like Jason's talks on CppCon. So was eager to check this channel out. Not quite what I was expecting. I have seen the gist of this topic explained in at least a dozen stackoverflow threads. So why the constant bombardments of on-site training? It's such a clear topic to explain through code example.