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

Sdílet
Vložit
  • čas přidán 26. 11. 2023
  • ☟☟ Awesome T-Shirts! Sponsors! Books! ☟☟
    Upcoming Workshop: Understanding Object Lifetime, C++ On Sea, July 2, 2024
    ► cpponsea.uk/2024/sessions/und...
    Upcoming Workshop: C++ Best Practices, NDC TechTown, Sept 9-10, 2024
    ► ndctechtown.com/workshops/c-b...
    Calling all C++ developers: A free preview of CLion with much faster core IDE functions is out! 🎉 Introducing CLion Nova - a version of CLion with the C++ language engine from ReSharper C++ and JetBrains Rider. It brings:
    Faster highlighting speeds
    A more responsive UI
    Significantly fewer freezes and hangs in refactorings
    Learn more and use it for free: jb.gg/cpp_nova
    Episode Notes: github.com/lefticus/cpp_weekl...
    Episode 125 - The Optimal Way To Return From A Function - • C++ Weekly - Ep 125 - ...
    T-SHIRTS AVAILABLE!
    ► The best C++ T-Shirts anywhere! my-store-d16a2f.creator-sprin...
    WANT MORE JASON?
    ► My Training Classes: emptycrate.com/training.html
    ► Follow me on twitter: / lefticus
    SUPPORT THE CHANNEL
    ► Patreon: / lefticus
    ► Github Sponsors: github.com/sponsors/lefticus
    ► Paypal Donation: www.paypal.com/donate/?hosted...
    GET INVOLVED
    ► Video Idea List: github.com/lefticus/cpp_weekl...
    JASON'S BOOKS
    ► C++23 Best Practices
    Leanpub Ebook: leanpub.com/cpp23_best_practi...
    ► C++ Best Practices
    Amazon Paperback: amzn.to/3wpAU3Z
    Leanpub Ebook: leanpub.com/cppbestpractices
    JASON'S PUZZLE BOOKS
    ► Object Lifetime Puzzlers Book 1
    Amazon Paperback: amzn.to/3g6Ervj
    Leanpub Ebook: leanpub.com/objectlifetimepuz...
    ► Object Lifetime Puzzlers Book 2
    Amazon Paperback: amzn.to/3whdUDU
    Leanpub Ebook: leanpub.com/objectlifetimepuz...
    ► Object Lifetime Puzzlers Book 3
    Leanpub Ebook: leanpub.com/objectlifetimepuz...
    ► Copy and Reference Puzzlers Book 1
    Amazon Paperback: amzn.to/3g7ZVb9
    Leanpub Ebook: leanpub.com/copyandreferencep...
    ► Copy and Reference Puzzlers Book 2
    Amazon Paperback: amzn.to/3X1LOIx
    Leanpub Ebook: leanpub.com/copyandreferencep...
    ► Copy and Reference Puzzlers Book 3
    Leanpub Ebook: leanpub.com/copyandreferencep...
    ► OpCode Puzzlers Book 1
    Amazon Paperback: amzn.to/3KCNJg6
    Leanpub Ebook: leanpub.com/opcodepuzzlers_book1
    RECOMMENDED BOOKS
    ► Bjarne Stroustrup's A Tour of C++ (now with C++20/23!): amzn.to/3X4Wypr
    AWESOME PROJECTS
    ► The C++ Starter Project - Gets you started with Best Practices Quickly - github.com/cpp-best-practices...
    ► C++ Best Practices Forkable Coding Standards - github.com/cpp-best-practices...
    O'Reilly VIDEOS
    ► Inheritance and Polymorphism in C++ - www.oreilly.com/library/view/...
    ► Learning C++ Best Practices - www.oreilly.com/library/view/...
  • Věda a technologie

Komentáře • 170

  • @jef777
    @jef777 Před 5 měsíci +177

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

    • @AtomicAndi
      @AtomicAndi Před 5 měsíci +3

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

    • @PhilHord
      @PhilHord Před 5 měsíci +7

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

    • @AtomicAndi
      @AtomicAndi Před 5 měsíci +2

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

    • @Evan490BC
      @Evan490BC Před 2 měsíci +2

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

    • @AtomicAndi
      @AtomicAndi Před 2 měsíci +1

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

  • @treyquattro
    @treyquattro Před 5 měsíci +31

    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.

  • @edubmf
    @edubmf Před 5 měsíci +53

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

    • @treyquattro
      @treyquattro Před 5 měsíci +2

      best of luck everyone I think

    • @tokyospliff
      @tokyospliff Před 5 měsíci

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

    • @Evan490BC
      @Evan490BC Před 2 měsíci +1

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

  • @not_ever
    @not_ever Před 5 měsíci +52

    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 Před 5 měsíci +10

      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 Před 5 měsíci +2

      clang also have those flags

    • @not_ever
      @not_ever Před 5 měsíci +1

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

    • @Febbe1
      @Febbe1 Před 5 měsíci

      @@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 Před 5 měsíci +7

      @@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.

  • @KennyMinigun
    @KennyMinigun Před 5 měsíci +48

    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 Před 5 měsíci +2

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

    • @antagonista8122
      @antagonista8122 Před 5 měsíci +10

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

    • @Febbe1
      @Febbe1 Před 5 měsíci +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 Před 5 měsíci +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 Před 5 měsíci +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).

  • @garyp.7501
    @garyp.7501 Před 5 měsíci +1

    Thanks! That's a very clear explanation.

  • @abdullahcnar2227
    @abdullahcnar2227 Před 5 měsíci +1

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

  • @graham12345dd
    @graham12345dd Před 3 měsíci

    Awesome as usual!

  • @christopherhayden8442
    @christopherhayden8442 Před 5 měsíci +26

    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 Před 5 měsíci +4

      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 Před 5 měsíci +2

      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 Před 5 měsíci

      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 Před 5 měsíci +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  Před 5 měsíci +5

      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."

  • @kuhluhOG
    @kuhluhOG Před 4 měsíci +5

    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 Před 21 dnem

      really? why would they do that?

    • @angelcaru
      @angelcaru Před 16 dny +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 Před 16 dny

      Two the two people here, see my edit.

    • @angelcaru
      @angelcaru Před 15 dny +1

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

  • @coding_with_thomas
    @coding_with_thomas Před 5 měsíci +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

  • @sofasurfer3793
    @sofasurfer3793 Před 5 měsíci +10

    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 Před 5 dny

    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.

  • @kimhyunpil
    @kimhyunpil Před 2 měsíci

    amazing!

  • @marcobonizzi3628
    @marcobonizzi3628 Před 5 měsíci +1

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

  • @davidmccormack99
    @davidmccormack99 Před 4 měsíci +3

    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  Před 4 měsíci +1

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

  • @tim37021
    @tim37021 Před 3 dny

    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.

  • @TsvetanDimitrov1976
    @TsvetanDimitrov1976 Před 5 měsíci +11

    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 Před 5 měsíci +7

      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 Před 5 měsíci +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 Před 5 dny

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

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

  • @gabrielb.2886
    @gabrielb.2886 Před 5 měsíci +3

    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?

  • @christiandaley120
    @christiandaley120 Před 5 měsíci

    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 Před 5 měsíci

      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 Před 5 měsíci +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  Před 5 měsíci +1

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

  • @syborg64
    @syborg64 Před 3 měsíci +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

  • @systemdeadlock
    @systemdeadlock Před 5 měsíci +3

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

    • @danielrhouck
      @danielrhouck Před 5 měsíci

      -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  Před 5 měsíci +1

      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 Před 5 měsíci

      Thanks Jason @cppweekly

  • @mytech6779
    @mytech6779 Před 5 měsíci +3

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

  • @jaybee9054
    @jaybee9054 Před 5 měsíci +1

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

  • @fcolecumberri
    @fcolecumberri Před 5 měsíci +7

    Happy "Page not found" Episode.

  • @fishhead314
    @fishhead314 Před 5 měsíci +1

    Short and sweet.
    Good content.
    +1

  • @eamonconway375
    @eamonconway375 Před 4 měsíci

    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 Před 4 měsíci

      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  Před 4 měsíci

      I need some more context for specific examples to discuss.

    • @eamonconway375
      @eamonconway375 Před 4 měsíci

      @@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;
      }

  • @roblayton3190
    @roblayton3190 Před 5 měsíci +9

    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.
    CZcams 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 CZcams 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 Před 5 měsíci +2

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

    • @roblayton3190
      @roblayton3190 Před 5 měsíci +5

      @@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.

  • @thomaswheeler4045
    @thomaswheeler4045 Před 5 měsíci +2

    excellent video.

  • @piotrbatko172
    @piotrbatko172 Před 5 měsíci +11

    A little bit too many ads for me.

  • @moretromain
    @moretromain Před 5 měsíci +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  Před 5 měsíci

      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.

  • @RicardoCapurro
    @RicardoCapurro Před 5 měsíci +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.

  • @Lecluyse2000
    @Lecluyse2000 Před měsícem

    Can anyone tell me why he uses puts instead of cout

    • @cppweekly
      @cppweekly  Před měsícem +1

      Fast compile times, smaller binaries, does exactly what I want, and makes for more readable assembly output.
      czcams.com/video/VZTVmKOXLVU/video.html

    • @Lecluyse2000
      @Lecluyse2000 Před měsícem

      Thank you very much@@cppweekly

  • @ryanjeffares8238
    @ryanjeffares8238 Před 5 měsíci +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 Před 5 měsíci +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 Před 5 měsíci

      @@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 Před 5 měsíci +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.

    • @user-cy1rm5vb7i
      @user-cy1rm5vb7i Před 5 měsíci

      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 Před 5 měsíci

      @@youtubeviewer7077 You could always test with Compiler Explorer.

  • @user-sj1yg1qt1g
    @user-sj1yg1qt1g Před 5 měsíci

    Is that RVO optimization?

    • @cppweekly
      @cppweekly  Před 5 měsíci

      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.

  • @Oaisus
    @Oaisus Před 5 měsíci +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 Před 5 měsíci +3

      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 Před 5 měsíci

      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.

  • @hairnetart
    @hairnetart Před 5 měsíci +7

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

  • @forgetfulfunctor1
    @forgetfulfunctor1 Před 5 měsíci +1

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

  • @embracevoid
    @embracevoid Před 5 měsíci +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 Před 5 měsíci +3

      Would you mind sharing your benchmark?

    • @embracevoid
      @embracevoid Před 5 měsíci

      ​@@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 Před 5 měsíci +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 Před 5 měsíci +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 Před 5 měsíci

      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

  • @reneb86
    @reneb86 Před 3 dny

    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.

  • @maliksmith8319
    @maliksmith8319 Před 5 měsíci +5

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

  • @adamkonrad
    @adamkonrad Před 5 měsíci

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

    • @cppweekly
      @cppweekly  Před 5 měsíci +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. :)

  • @jaorlowski
    @jaorlowski Před 3 měsíci

    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  Před 3 měsíci +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.

  • @alienpioneer
    @alienpioneer Před 3 měsíci +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.

  • @user-cy1rm5vb7i
    @user-cy1rm5vb7i Před 5 měsíci +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 Před 5 měsíci

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

  • @victotronics
    @victotronics Před 5 měsíci +9

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

  • @maxaafbackname5562
    @maxaafbackname5562 Před 5 měsíci

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

    • @cppweekly
      @cppweekly  Před 5 měsíci +1

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

  • @cmdlp4178
    @cmdlp4178 Před 5 měsíci +1

    Accidentally moved from unique_ptr variable, 404 not found

  • @wigi426
    @wigi426 Před 5 měsíci +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 Před 5 měsíci +1

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

    • @wigi426
      @wigi426 Před 5 měsíci +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 Před 5 měsíci

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

    • @if-constexpr
      @if-constexpr Před 5 měsíci +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  Před 5 měsíci

      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.

  • @tiagocerqueira9459
    @tiagocerqueira9459 Před 5 měsíci

    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.

  • @ItsBaffledd
    @ItsBaffledd Před 5 měsíci +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  Před 5 měsíci +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 Před 5 měsíci

      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

  • @vasilivoloshin
    @vasilivoloshin Před 5 měsíci

    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 Před 5 měsíci +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.

  • @TheMR-777
    @TheMR-777 Před 5 měsíci +3

    404 - Comment Not Found

  • @nicholaskomsa1777
    @nicholaskomsa1777 Před 5 dny

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

  • @samuelgunter
    @samuelgunter Před 3 měsíci +1

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

  • @CramBL
    @CramBL Před 5 měsíci +2

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

  • @thestarinthesky_
    @thestarinthesky_ Před 5 měsíci

    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 Před 5 měsíci +2

      More assembly code for similar result

    • @user-cy1rm5vb7i
      @user-cy1rm5vb7i Před 5 měsíci +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  Před 5 měsíci

      @@user-cy1rm5vb7i 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.

  • @iamgly
    @iamgly Před 5 měsíci +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 Před 5 měsíci +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 Před 5 měsíci

      ​@@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 Před 5 měsíci +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 Před 5 měsíci +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 Před 5 měsíci +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.

  • @itsreality3646
    @itsreality3646 Před 5 měsíci

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

    • @cppweekly
      @cppweekly  Před 5 měsíci

      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.

  • @Omnifarious0
    @Omnifarious0 Před 5 měsíci +2

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

  • @NombreNoBailable
    @NombreNoBailable Před 5 měsíci +1

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

  • @veetaha
    @veetaha Před 5 měsíci +1

    Waiting for your Rust course!

  • @djin81
    @djin81 Před 5 měsíci +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 Před 5 měsíci +2

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

  • @oschonrock
    @oschonrock Před 5 měsíci

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

  • @onehotseat
    @onehotseat Před 5 měsíci +1

    Shill moar!

  • @the0neskater
    @the0neskater Před 5 měsíci +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.

  • @lostphysicist
    @lostphysicist Před 5 měsíci

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

    • @stephenhowe4107
      @stephenhowe4107 Před 5 měsíci +4

      No, not at all

    • @TsvetanDimitrov1976
      @TsvetanDimitrov1976 Před 5 měsíci +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 Před 5 měsíci

      @@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 Před 5 měsíci +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 Před 5 měsíci +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.

  • @VioletGiraffe
    @VioletGiraffe Před 5 měsíci +2

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

    • @bloodgain
      @bloodgain Před 5 měsíci

      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.

    • @mborows2
      @mborows2 Před 5 měsíci

      Maybe for you. I really like Clion. (with old UI).

    • @_supervolcano
      @_supervolcano Před 5 měsíci +2

      CLion is a godsend.

    • @sergeykolesnik1171
      @sergeykolesnik1171 Před 5 měsíci

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

    • @adamkonrad
      @adamkonrad Před 5 měsíci

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