C++ Weekly - Ep 421 - You're Using optional, variant, pair, tuple, any, and expected Wrong!

Sdílet
Vložit
  • čas přidán 14. 05. 2024
  • ☟☟ 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...
    This episode is sponsored by think-cell. think-cell.com/cppweekly
    Episode details: github.com/lefticus/cpp_weekl...
    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 • 123

  • @gregorssamsa
    @gregorssamsa Před měsícem +23

    Should be fixed in C++29

  • @N....
    @N.... Před měsícem +35

    6:19 you can just write `std::in_place`, no need to write the extra `_t{}`

  • @josephnyu
    @josephnyu Před měsícem +23

    I think Jason missed a point, for functions returning expected it is very likely to have branching, and in that case RVO will use move constructor most of the time anyway.
    The same can be said for optional (indeed if your code always return a Lifetime then the return type should be Lieftime and not optional)

  • @MatthewWalker0
    @MatthewWalker0 Před měsícem +8

    I have thought about this a few times. And, every time I imagined what the "fewer moves" code would look look, it seemed like a premature optimization making the code less readable; it looked more like C89 code with the variables and return value declared at the beginning of the blocks. Much cleaner to just put a couple `if (bad) return std::nullopt` around.

  • @DamianReloaded
    @DamianReloaded Před měsícem +62

    This seems like something the compiler could optimize for.

    • @japedr
      @japedr Před měsícem +19

      Except when it cannot... as shown in the video. Any class without a trivial destructor is affected by this, and that includes all classes with a vector or a string as a member.

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

      @@japedr But isn't the reason why Jason's code with the extra constructor/destructor calls couldn't be optimized out was due to the fact that the all called print() ?
      When I checked out the Compiler Explorer instance that Jason linked in the GitHub page for this episode, I removed the body of the print() function and the resulting assembly became that of a barebones program. I'm kind of a noob at C++ so I might be missing something here.

    • @MantasXVIII
      @MantasXVIII Před měsícem +2

      ​@@japedryeah but haven't you heard? The compiler knows better than you. Premature optimization is the root of all evil....
      And other such nonsense from people who've worked on a serious project

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

      seems like it optimizes (prematurely) for an empty optional!?

    • @Muzzleflash1990
      @Muzzleflash1990 Před měsícem +3

      It does optimize it all. But it ofc not remove the observable side effect of printing.

  • @dookshi
    @dookshi Před 21 dnem +1

    Haha, I regret no episode of yours! In fact, you did such a good job explaining RVO so far that I did not find anything you did not already cover implicitly. You just made it explicit now. Excellent series!

  • @mjKlaim
    @mjKlaim Před měsícem +8

    Good to know, thanks for pointing this!

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

    I loved the end! Makes me subscribe.

  • @VoluneAneline
    @VoluneAneline Před měsícem +7

    I suspect a lot of people will write "return std::unexpected{"oh no!"};" and that will prevent RVO anyway (at least when I tried). I guess we will (again?) rely on tooling to fix these issues. If the compiler cannot optimize in the first place for most cases.

  • @gwendolynhunt2493
    @gwendolynhunt2493 Před měsícem +4

    Thanks Jason, now I have a couple hundred APIs in several libs I need to revisit. Really, an excellent YT. You do good work.

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

    Great episode!

  • @axelBr1
    @axelBr1 Před měsícem +27

    Self taught C++ in early 2000s, and have started to use it again, after work took me away from it. Have recently heard of RVO and have just started incorporating std::optional into a project I'm working on, as being able to say that there is no value to return is really helpful. And then I saw this.

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

      Run away while you can!

    • @DominicDW8
      @DominicDW8 Před měsícem +27

      Well, don't be too sad. It's oftentimes worth using the "inefficient" way to get more readable code. The "issue" shown in the video is certainly quite interesting, but also quite certainly not a deal breaker for performance in the vast majority of use cases.

  • @LesleyLai
    @LesleyLai Před měsícem +57

    This makes me appreciate Rust's "move by default." While annoying in other areas, that semantics means the compiler can optimize this kind of code well and we don't need to think about this issue

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

      with the difference that the value gets moved (e.g. 10:05)

    • @niklkelbon3662
      @niklkelbon3662 Před měsícem +12

      )) rust has no RVO and copy elision, values will be moved (memcpy) every time. So, C++ better here

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

      ​@@niklkelbon3662C++ is only better here is written perfectly. Rust - as usual - has vastly superior default behav

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

      ​@@niklkelbon3662right, but for something like a string memcpy is not really an issue..? You only copy the pointer, not the buffer

    • @tiagocerqueira9459
      @tiagocerqueira9459 Před měsícem +5

      ​@@niklkelbon3662 llvm does RVO, but the language and Mir can't guarantee that

  • @sadunozer2241
    @sadunozer2241 Před měsícem +4

    I just want you to, I don’t know… regret watching this episode
    Jason Turner breaks 2024

  • @PaulMetalhero
    @PaulMetalhero Před měsícem +10

    +1 for the cppreference dark theme!

  • @Muzzleflash1990
    @Muzzleflash1990 Před měsícem +3

    To the people thinking this is about RVO (copy elision): it is not. And by the way RVO works fine (which is why you don't see extra outputs from an internal copy of Lifetime when the optional is copied out as a return value: because it is not).
    The problem is when you have a Lifetime object but what you really want to return is an Optional which implicitly causes the Optional constructor to be called with a Lifetime. As this point (language-wise) the Lifetime must be materialized because the parameter is a glvalue. So Lifetime{42} is materialized as the parameter. Then the inner Lifetime is move constructed from that.
    The issue here is nothing to do with RVO, but with calling functions (constructors) to make a class B by passing an A and expecting the construction of A to be elided; which is won't. The resulting Optional is perfectly NVRO'ed (which is actually an optional optimization) in the first example; or (unnamed) RVOed out in the remaining cases. And RVO has nothing to do with the constructor/destructor being trivial or not (if elided those copies never existed at all!). For example: godbolt.org/z/fMKzxEbj6 .
    But can't the compiler optimize it away anyway? In many cases: probably sure. What it can't optimize away is the observable side effect of outputting.

    • @sirhenrystalwart8303
      @sirhenrystalwart8303 Před 28 dny

      When is it not allowed to elide the side effect of printing? RVO works fine in all these cases if your remove the optional.

    • @Muzzleflash1990
      @Muzzleflash1990 Před 28 dny

      @@sirhenrystalwart8303Basically, eliding is allowed (and required) when returning unnamed temporaries of the same type as the return value. If you remove the optional then RVO triggers since you are just returning the Lifetime (prvalue) you construct: no intermediate is constructed; only the final value. As the R in RVO hints it works for Return values, but following has nothing to with RVO.
      The problem is that binding references to temporaries force the construction of the referred to object. When you return an Optional you have to go through the constructor of optional (any function really) that has a named parameter compatible with the inner value. These parameters are references that bind to the prvalue, and by the spec, then the value must be materialized, and thus the printing must be shown.

  • @volta948
    @volta948 Před měsícem +24

    What about std::make_optional?

    • @furuthebat
      @furuthebat Před měsícem +9

      ```
      std::optional get_value()
      {
      return std::make_optional(42);
      }
      ```
      this works.
      ```
      std::optional get_value()
      {
      Lifetime l;
      return std::make_optional(l);
      }
      ```
      Same result as with copy-ctor

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

      afaik make_optional simply calls the constructor of T with all arguments forwarded to it.
      So in the first case you're actually calling Lifetime(int) and in the second Lifetime(const Lifetime &), I.e. the copy constructor

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

    Would love if you could make a video on book recommendations for C++ in 2024

  • @aniketbisht2823
    @aniketbisht2823 Před měsícem +12

    This is really unfortunate. I think you should discuss this with Arthur O Dwyer. He has proposed many papers on topic of returning objects (and implicit moves) and has also given talks on this subject. This seems like a missed optimization opportunity as far as core language is concerned. This case is very similar to the C++17 guaranteed copy-elision. We can generalized that approach to elide copying any object/value that is used to construct the returned object (given the converting constructor of the returned type itself is not explicit).

    • @jirihavel9766
      @jirihavel9766 Před 29 dny

      It's not just missed optimization. All these prints are observable side effects that optimizer can't remove. We may need one of those :
      - print that can be removed by the optimizer (must be explicitly allowed by the standard)
      - removing any side effects from copy constructors (i.e. copy constructor is only allowed to do copying) This however leads deep into the "undefined behavior" territory.
      - explicitly allow copy ellision or return value optimization in these situations.
      All are decisions in the standard. Compiler authors aren't allowed to do this on their own.

  • @Psy45Kai
    @Psy45Kai Před měsícem +10

    I think there is a problem with the test setup here:
    The Lifetime class always have an IO side effect. These cannot be optimized away by the compiler properly! But classes following the rule of zero might not be affected ☝️ I would like to see a follow-up with optional and optional analysing the asm.

    • @Muzzleflash1990
      @Muzzleflash1990 Před měsícem +4

      You have to be careful to distinguish the guaranteed "copy elision" conditions with general optimization. In fact, if the guaranteed copy elision rules apply then the side effect MUST NOT happen due to the delayed materialization. Suppose you did: Lifetime x = f() where f() function returns a Lifetime (not inside optional or anything) simply by: return Lifetime{42}; then there MUST only be single construction since C++17.
      The problem is none of these guaranteed copy elision rules apply here. The constructor for optional or the others, take a reference (&&) to the contained type forcing the materialization of an extra Lifetime. So in terms of language semantics it must create the object. You are then right, that under the as-if rule the compiler could automate the copy away, if it did not have a side effect.

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

    This strikes me as something that could potentially have been dealt with via a more explicit Some(foo) and None design for these container types, but I haven't thought through it to pick out whether that's true.

  • @avramlevitter6150
    @avramlevitter6150 Před měsícem +4

    Something I'm noticing: your Lifetime class is great at finding out when you might be calling an expensive operation that can't easily be optimized out.
    When you're using this for a pure value type this all becomes a non-issue.
    If you have a complicated type, it might be worth seeing if there's an empty state that can be defined and return that (e.g. an empty vector).
    If you have a complicated type, that's expensive to move, and can't easily have an empty state and you want to add the empty state by sticking it into an optional... then yeah, this is a problem. Personally I haven't run into it yet but that doesn't mean it's not someone else's use case.

  • @davidfong
    @davidfong Před měsícem +9

    9:47 did you mean to say "more and more implicit conversions" instead of "explicit"?

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

      Yes, I did mean implicit

  • @az-kalaak6215
    @az-kalaak6215 Před měsícem

    well, to be honest I always use optional / variant as a way to return when exceptions is not needed, but code can still fail. but I don't mark the parameter as explicit as those error structs that I use are only used in this context. so no issue for me, but still interesting!

  • @FixedTypo
    @FixedTypo Před měsícem +2

    We often face the situation that our objects are returned by some sort of factory function before being placed into the optional, etc. We came up with a solution to avoid moves here. Basically all of these classes have constructors overload that participate if the contained value is constructible from another object. So we created a helper class that holds a callable and defines a conversion operator to the result type of the callable by calling it (so we get rvo there). With this class, a helper function and a lambda wrapping the original function call we were able to emplace the return value of the function directly into an optional. We did introduce a macro as we did not find it too readable though.

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

      Is this similar to the superconstructing super elider technique that Arthur O'Dwyer outlined in his blog?

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

      @@StanleyPinchak Yes it is actually equivalent to the class with_result_of_t shown in Arthur‘s blog. After hiding the verbosity of invoking the constructor of std::optional with the class and the lambda behind a macro MAKE_OPTIONAL it is actually quite „pretty“. Usage looks like this:
      MAKE_OPTIONAL(LifeTime{42})
      MAKE_OPTIONAL(createLifeTime())
      The nice thing is, that you can get direct member initialization too.

  • @erikgrundy
    @erikgrundy Před měsícem +2

    I know C++, kind of, but haven't actually used it in any real applications, so I don't really experience the issues I sometimes see talked about, like this one. So for those more experienced than me: how often do these sorts of things end up mattering? A lot of online C++ content loves to talk about small ways of squeezing a little bit more performance out of your code. These sorts of micro-optimisations don't strike me as the thing that will make the difference between your code being acceptably fast or not.
    E.g. the specific problem presented in this video. I would think that the majority of the classes you write in an application don't have anything fancy going on in their various constructors, assignment operators, and destructors. So I wouldn't think that this would make an enormous difference.
    Would it make your application faster? Probably. But I would think that the things to target when it comes to optimisation would be less granular.
    I'm happy to be wrong, so feel free to challenge my assumptions. I'm genuinely looking for an answer for why the C++ community seems focused on optimisation to such a degree.

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

      I think this is just inherent to C++ as a language where you can almost always avoid paying costs like execution time and/or memory usage, if they aren't strictly needed. You're entirely right that this kind of thing will not typically matter to a specific application, but on the other hand, as a library writer you don't want to force unnecessary pessimisations on your users. And as a language designer you want to make it easy for library writers and other users to avoid those. I think that's at the core of Jason's criticism here.
      Although I don' t teach programming, the only thing I would ever teach about optimisation is that you just have to profile your code if performance is a concern. Especially with C++, it's almost impossible to predict what an optimising compiler will produce, so it's better to observe general guidelines for readable, maintainable code and avoid premature optimisation.

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

      Definitely not as good as some people commenting under these videos, but having used C++ in personal projects and work...I don't think you think about the bigger picture. The underlying object could represent anything and be called from anywhere. Once this function is called in a loop in some hot path, the performance impact is non-negligible.
      I remember refactoring some code and overlooking that I passed a copy of a vector to some lambda by mistake; or something similar. Rookie mistake. Didn't realise it until I benchmarked and found out that the code is like 5x slower. Might sound like a lot, but if it takes 50ms instead of 10ms, you probably won't notice until you actually benchmark it. Returning a copy from optional/variant/... like shown in the video could have the same impact. Moving instead of RVO might not be that big of a problem, but still - it's unnecessary.
      I'd say that the feeling that the C/C++ community is more focused on optimisation is because that's just the kind of stuff that's mostly written in C/C++. There isn't much of a reason to use it for other stuff; or rather there are better languages/tools (that might just be a wrapper over some C/C++ libraries and therefore the "hot paths" being performant anyway - so why make your life difficult?).

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

      @@JanFlunher but you only discovered your error when benchmarking, which i think is the right time.
      i don't dispute that this *could* have a performance problem, and indeed i think it's not unreasonable to use the faster version in all cases - it's not notably less readable. my observation is more generally about the c++ community, who (in my observation) will often claim that you should always prefer x to y in all cases, without considering that y may be more maintainable, readable, etc.
      c++ is used in lots of high performance scenarios. but it's also used in places where code readability and correct abstractions are important. this was one of the core principles of c++. and yet i see people reject an idea that improves readability solely on the idea that it *might* worsen performance. you can only know that by testing it, rather than assuming that it will make your code unbearably slow.

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

      ​@@erikgrundy Yes, I discovered it, but I don't always benchmark all of my code. And in a big code base, that might be riddled with stuff like this, it will definitely become a problem for not-so-obvious reasons. Small performance issues become big ones once they come in herds.
      Can't speak for the community as a whole. But I personally like to always write the most performant code in most cases, because it's a good training for when it actually matters...and saves a lot of headaches later. Though the "most performant" is highly subjective obviously. Adding an explicit "std::optional(...)" isn't a deal breaker for me here. Doing everything with template meta-programming is a big nope. I'm still a fan of readable code.
      The thing in this case is that not only it *might* worsen the performance, but it always *will* and it's not very obvious. If it's negligible or not, depends.

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

      I don't consider this a micro optimization, I consider it a matter of understanding what your tools are doing.
      This is the kind of thing that, once I understood it, I got about a 100x performance improvement in my scripting engine. It was a kind of "death by 1000 cuts"
      In my experience this is where most performance is lost in a C++ program, but people don't think about it and instead do things like inline assembly to try to get an extra bit of performance out.
      czcams.com/video/lNnBExDoNSQ/video.html

  • @N....
    @N.... Před měsícem +3

    Great video. It's weird that the explicit vs non-explicit constructors seem to be reversed, we can't just write `return {std::in_place, 1};` for best results, but writing `return Lifetime{1}` is allowed and does the wrong thing. I am curious what the committee reasoning is for this strange behavior, I guess maybe they were worried about if the wrapped type also took `std::in_place`

  • @Lol-ld2lu
    @Lol-ld2lu Před měsícem +2

    I don't think that calling a move constructor once is worse, than throwing an exception. Move constructor or operator= by their intent should be lightweight. This situation however is not great, but neither as bas as you describe it.

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

    This episode makes me feel better about my rampant use of raw pointers

  • @erroneum
    @erroneum Před měsícem +2

    I wonder how hard it would be for a compiler to implement a flag that explicitly says you want implicit conversions to standard library containers to fail when it would force an extra object. Obviously that wouldn't be standard compliant behavior, but that's the point of it being a flag (just like -fno-exceptions or -fno-rtti).

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

      You could propose it as a part of profiles proposal, then it could be in the standard.

  • @michaelwillett7084
    @michaelwillett7084 Před měsícem +4

    Ah, I've run into this problem before trying to write unit tests for my own version of std::expected a couple years ago!
    The problem I ran into is the fact that you are creating an object like Lifetime to track your constructors and destructors / move operators. In doing so, these functions have side effects that the optimizers will explicitly NOT remove, forcing the duplicate constructors and destructors. If you don't have the observers, and just check the pure ASM, then you realize the RVO can kick in and you don't get the extra function calls.
    I admit that this doesn't help in cases where you do have classes with custom move assignment / construction / destructors, and by definition we end up with the behavior outlined in this video. It doesn't change your point, but there are a lot of cases with default constructors that the simplified code will run just fine with no additional overhead.

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

    Quite frankly, if it comes to exceptions, I use them for truly exceptional things. But errors are not necessarily exceptional. In quite some cases they are even expected (or at least something which you SHOULD expect). And exceptions are ergonomically REALLY stupid for that and express the intent just way worse than something like std::expected. Disconnecting the "happy" and the "error" path is not always the good thing to do.
    And expressing programmer intent in code is for 99% of code more important than efficiency, at least imo.

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

      Any generic result container stands and falls with the level of template deduction the language is capable of. Needing to explicitly type a deeply nested type is a nightmare. I don't know how good cpp is but java has one of the worst. It brakes after 2-3 levels of "templates" requiring a hint in order to compile.

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

    Cannot be catch using static analysis like a rule for clang-tidy?

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

    I find it interesting that, at 6:48, if you had used an auto return type you would've gotten the behavior you're looking for just coincidentally. Dunno if that's a meaningful observation but I think it makes me want to keep using auto, especially since I don't want to unnecessarily type the return type twice personally.

  • @johnymag666
    @johnymag666 Před měsícem +2

    Is there a compiler flag to get a warning for these scenarios?

  • @MichaelLauerDr
    @MichaelLauerDr Před 24 dny

    Thanks for this insightful episode. Whenever I want to like C++, they‘re doing something like that. 😢

  • @garrettkajmowicz
    @garrettkajmowicz Před 20 hodinami

    If you didn't have the operations being performed in the various constructors, would the compilers be able to optimize those operations away?

  • @X_Baron
    @X_Baron Před měsícem +10

    It was probably a mistake to make these part of the standard library, instead of language features. Interesting to see if anyone comes up with a workaround.

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

      And we could also have built-in arrays that don't have a bunch of pitfalls...

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

    I learned something ! Again !
    Although not sure I'll ever use it.
    Am I a bad C++ programmer for avoiding std:: as often as I can ? I place clarity and readability of code over anything else.
    "Ce que l’on conçoit bien s’énonce clairement. Et les mots pour le dire arrivent aisément"
    And I never felt any clarity in the use of std:: or the huge error cascades that are (were now days isn't it ?) generated when compiling typos.

    • @Tibor0991
      @Tibor0991 Před 23 dny

      Yes, you are bad, you're writing C++ wrong and decided to cop out of your guilt by shifting the blame to the STL instead. I bet your code is not as clean and readable as much as you think it is and I'm pretty sure I'll bleed from my eye sockets if I ever see it.

    • @pandacongolais
      @pandacongolais Před 23 dny

      @@Tibor0991 Thanks, time to become a team leader and tell others their code is bad

    • @Tibor0991
      @Tibor0991 Před 23 dny

      @@pandacongolais that's literally what happened to my workplace (before he burned out and left).
      But srsly, "you don't pay for what you don't use" and the STL rolls by that.

  • @Tibor0991
    @Tibor0991 Před 23 dny

    Are you 100% certain sure that the print() function isn't making the compiler opting out of optimizations due to unoptimizable side effects?

    • @cppweekly
      @cppweekly  Před 15 dny

      If the types are trivial it all becomes irrelevant, but people rarely go out of their way to make sure the types they use are trivial.
      If the types are not trivial, normal lifetime rules come into play, this I am 100% certain of. Check out this other video: czcams.com/video/bpF1LKQBgBQ/video.html

  • @bruderdasisteinschwerermangel

    I guess this is what make_optional and the like are here for
    Just a shame theyre annoying to use because of just the way they look and also giving you horrible compiler errors when the ctor signature changes

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

    I wonder if there are similar problems in Rust

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

    Couldn't RVO be generalized to handle these cases with "nested" types? Not changing how i use optional etc. it's not worth it except in very rare cases.

  • @xwize
    @xwize Před měsícem +5

    If everyone is using it wrong, what does that say about the language

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

    That’s crazy

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

    Oh dear.. that *is* quite unfortunate.

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

    Why there are no `make_optional`, `make_variant`, `make_expected`, `make_unexpected` factory functions? Just like `make_pair`. Function template argument deduction would kick in and I need to write less template stuff. Or is CTAD solving this? Or is thiss non-issue to begin with?

    • @anon8510
      @anon8510 Před měsícem +6

      make_optional exists, at least

  • @TsvetanDimitrov1976
    @TsvetanDimitrov1976 Před měsícem +7

    Would using some helper function, like make_optional, or make_expected fix this?

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

      make_optional actually exists. But it's just even more to type, an additional make_

    • @TsvetanDimitrov1976
      @TsvetanDimitrov1976 Před měsícem +6

      @@BaardFigur it actually saves some typing sometimes. For example std::optional{std::in_place_t{}, 42} vs std::make_optional(42)

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

      Yeah, good point

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

    Couldn't you just write `return {Lifetime(42)}`? Same for the std::expected case, just braces around `l` should work?

  • @dshvets1
    @dshvets1 Před měsícem +9

    Wouldn't make_optional() be a better option?

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

      afaik make_optional() need a *public* constructor for the type, which can be an issue

    • @9uiop
      @9uiop Před měsícem

      Isn't that practically the same as he did with the one-liner? I guess template argument deduction cannot apply here, so make_optional is even more lengthy to type

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

    6:38 the constructor taking std::inplace_t should also be conditionally explicit. It's tedious to write the whole type name. I usually prefer just returning the value_type of optional but as this video suggests, it might not be as optimal. So std::inplace_t constructor should be used for "non-trivial" value_types but that constructor shouldn't really be explicit.

    • @N....
      @N.... Před měsícem

      You can just write `std::in_place`, no need to write the extra `_t{}`, but yes I agree it would be nice to just write `return {std::in_place, 1};`

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

    This really does seem like a language defect. I haven't looked through the papers for these facilities yet but surely they must have at least thought about this issue? There must have been some way that would not have made this an issue even if it qould have required more involved changes

  • @somerandompersonintheinternet
    @somerandompersonintheinternet Před měsícem +6

    I do regret watching this episode.

  • @Mike-gs7eo
    @Mike-gs7eo Před měsícem +1

    What happened to good defaults?

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

    Interesting issue. How about declaring the return object as "decltype(*this)"?

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

    std::make_expected

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

    Yeah... I do regret watching this video. I went from, oh interesting, oh do I have problems I need to fix now? to oh wait... no, implicit brace initialization works, he had to make it explicit for it to not work... so... I think we are fine? Wait... what did I learn from this? I guess I gained worry that I am missing something?

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

      Well, Print(const std::source_location& location = std::source_location::current()) was cool, I'd never seen that before.

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

    This is one of the things that I've always hated about C++ and that I've hopefully made better in my own language with more inference. I guess you could say in my language I infer all the things.

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

    Regretted watching this, hahaha

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

    No, I'm not.

  • @DerAlbi
    @DerAlbi Před měsícem +6

    Btw, RVO is still working! After all, the return value is not copied. What is copied/moved is the thing that belong into the return value.
    To say that RVO is disabled in these cases is wrong.
    But overall, you are not wrong with the video. It is not zero-cost abstraction if the actual use-case (and intended use) is not zero overhead. These features basically violate c++.

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

    why did you return {42} and not just 42?
    same for expected
    just do a
    if (foo)
    return 42;
    else
    return "not foo";
    should work and call the right constructor of explicit (once)

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

    4:30 - return 42

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

    What a mess

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

    That's why any serious library or project are written in C, not C++. You cannot expect what instructions compiler will generate without going insane with C++

  • @nickkallen1
    @nickkallen1 Před měsícem +2

    This is a pretty annoying and significant limitation :(. Accidentally calling copy constructors is one of the major pitfalls of c++

  • @Debrugger
    @Debrugger Před 28 dny

    This language is insane lol

  • @leshommesdupilly
    @leshommesdupilly Před měsícem +2

    Virgin std::variant fan vs Chad void* + c-style cast enjoyer

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

      While using heap to allocate your object (that you're returning a void* pointer to). Yeah, right.

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

      always resort to the 20-50 constructs of C instead of learning newer useful constructs. make sure to dismiss them as idiomatic C++ complexity too...
      (don't take it personally, I never meant it. I understand your comment's meant to be a joke. I just want to say it's a bad joke :P )

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

      @@marce3893You are right and wrong at the same time (in a nice Schrödinger-esque way) 🙂. C++ does automate many things *but* the idiomatic complexity of C++ is indeed insane!

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