C++ Weekly - Ep 231 - Multiple Destructors in C++20?! How and Why

Поділитися
Вставка
  • Опубліковано 18 жов 2024

КОМЕНТАРІ • 122

  • @gnulinux2000
    @gnulinux2000 3 роки тому +10

    One word wow.... C++ going so fast and so vast, it just feels like we always catching up

  • @goodecheeseburgers6320
    @goodecheeseburgers6320 4 роки тому +65

    Holy shit Jason the learning curve only gets curvier.

    • @arthopacini
      @arthopacini 4 роки тому +7

      we might get some c++ mastering speedrunning

  • @-taz-
    @-taz- 4 роки тому +39

    "Choose! Choose the form of the destructor." -Ghostbusters

    • @slipcurve1410
      @slipcurve1410 4 роки тому +3

      { crossTheBeams(); } is non trivial

  • @guest1754
    @guest1754 4 роки тому +10

    I've been out of the loop in C++ development for a few years already and I'm just learning that concepts finally made it into C++20. That, and there's GCC 11. Shit's bananas.

  • @TNothingFree
    @TNothingFree 4 роки тому +12

    Was expecting some shenanigens, wasn't dissappointed,
    Very intereseting thanks!

  • @LesleyLai
    @LesleyLai 4 роки тому +39

    This is insanely good. Though it makes me sad that STL implementations may not use it for backward compatibility.

    • @auxchar
      @auxchar 4 роки тому +5

      I suppose for backward compatibility, they could do it the inheritance way.

    • @LesleyLai
      @LesleyLai 4 роки тому +4

      @@auxchar Or some ugly versioning macro and manage two implementations (msvc does that in some places.)

    • @sephirostoy
      @sephirostoy 4 роки тому +5

      @@LesleyLai Msvc team plan to make a big update of their toolset to solve many known problems but require breaking changes. So this may happen in the future, but I don't know when. My guess is that it might happen when the STL will get modularized.

    • @MrSamkots
      @MrSamkots 4 роки тому +2

      Can you please explain how that will break backword compatibility?

    •  3 роки тому

      Macros

  • @johnjohnson7070
    @johnjohnson7070 2 роки тому

    This is not "multiple destructors".
    An Optional only has one destructor (a trivial destructor) and an Optional only has one destructor (a non trivial destructor)!
    However there are multiple destructors in C++. Try to look at the generated assembler for a classes destructor for a class with non-trivial destructible members.

  • @ultimatesoup
    @ultimatesoup Рік тому

    Wow, that is really cool

  • @ujosnah
    @ujosnah 4 роки тому

    In any case you end up with user-declared destructor. In such case compiler does not generate implicit move constructor for you. Therefore, you have to define move constructor explicitly, otherwise your optional is not movable. So, is there a way to not define `~Struct() = default` at all?

  • @SalingToMars
    @SalingToMars 4 роки тому +2

    Regarding the audio clicking sound. Have you tried increasing the buffer size in whatever software you use to record? If the buffer is too small, then every once in a while the computer won't be able to process a buffer completely in time and this results in random clicks in the recording. I've definitely been there. Super annoying, super random

  • @CodingJesus
    @CodingJesus 4 роки тому +1

    Great video. Very well explained!

  • @olafurw
    @olafurw 4 роки тому +16

    Black screen around 6:22, and again at around 10:03

    • @CryZe92
      @CryZe92 4 роки тому +5

      @@cppweekly I'm on UA-cam Premium and yeah I get the black cuts as well.

    • @VioletGiraffe
      @VioletGiraffe 4 роки тому +2

      @@void_p, same for me. Must be our ad blockers.

    • @Theodorlei1
      @Theodorlei1 4 роки тому +4

      Standard UA-cam, no ad blocker here, seeing the black screens on chrome desktop

    • @juliankandlhofer7553
      @juliankandlhofer7553 4 роки тому +1

      saw the ads and the black screen on mobile

    • @daantimmer
      @daantimmer 4 роки тому

      Am on premium, mobile, saw two black screens. I am behind a PiHole though, but premium shouldn't even try to load ads. (Never seen this behaviour before)

  • @YourCRTube
    @YourCRTube 4 роки тому

    Great episode. Finally some more advanced material in Weekly.

  • @jasoncole7711
    @jasoncole7711 2 місяці тому

    Really this should be called "Conditional Inheritance" ;)

  • @larswadefalk6423
    @larswadefalk6423 3 роки тому

    Now I must have chosen the wrong Jason turner weekly. This one flew right over my head. I can't still wrap my head around its necessity. But, well, thanks anyway.

  • @Wimpielef
    @Wimpielef 4 роки тому +10

    Because of the anonymous union the data destructor is not called I guess?
    Or does the union serve another purpose?

    • @LesleyLai
      @LesleyLai 4 роки тому +16

      Yeah. The purpose of the union is to circumvent the default construction of the value and the unconditional invocation of the value destructor since we don't know whether an optional contains a value at compile time. Also, an optional can contain types without a default constructor.

  • @kwkfortythree39
    @kwkfortythree39 4 роки тому +10

    Can this be applied to any function member or only to destructors?

    • @sephirostoy
      @sephirostoy 4 роки тому +17

      The require clause can be applied to any function (member and non-member).

  • @STOJATEK
    @STOJATEK 3 роки тому

    That's actually pretty awesome, thanks for sharing!

  • @Sonnentau1
    @Sonnentau1 4 роки тому +2

    I don't get, how you access the destructor of data in the struct optional_nontrivial. (9:45 at line 8)
    The union with the data member is defined in optional, and optional inherits from optional_nontrivial not the other way round.

    • @Sbence92
      @Sbence92 4 роки тому +5

      The static_cast casts the this ptr to the derived type that's injected via template param, that's why data is accessible. This technique is called static polymorphism. Neat little trick.

    • @MrSapps
      @MrSapps 4 роки тому +1

      using CRTP

  • @matt_7670
    @matt_7670 4 роки тому +1

    Great video. Btw seems like some portions you were holding arrow keys to move characters. Ctrl+arrow (I think alt+arrow on mac) jumps by word.

  • @rajatgirotra
    @rajatgirotra 4 роки тому

    Thanks Jason for this insight..!!
    Really nice and to the point video.

  • @tzlom
    @tzlom 4 роки тому

    Apparently, code on 9:40 can't be compiled with Optional being instantiated. Because of the union for non-trivial destructible type and because of line 8 where unknown name Contained being used. But that is not my question, my question is - in the presented shape code does compile, that mean that gcc does not fully figured out how exactly you can destruct the object, but already knew that is is not trivially destructible. That level of laizyness I was not expecting, do you have any other examples or explainations how it works under the hood?

  • @6754bettkitty
    @6754bettkitty 4 роки тому +2

    Whoa! Mind blown!

  • @minirop
    @minirop 4 роки тому +2

    Instead of conditional inheritance, wouldn't enable_if be enough? And it would simulate the requires version mopre closely.

    • @xavierthomas1980
      @xavierthomas1980 4 роки тому +3

      Not naively, SFINAE would not work in this case, because "Contained" is a template parameter of the "Optional" class template and not of a member function (destructor) template. One way to make it work with SFINAE would be to templatize the destructor using a dummy template parameter that default to "Contained":
      template
      class Optional {
      ...
      template
      ~Optional() {
      ...
      }
      }

    • @LEpigeon888
      @LEpigeon888 4 роки тому +2

      @@xavierthomas1980 Destructors can't be templated, i get this error : "error: destructor 'Optional::~Optional()' declared as member template".

  • @jewulo
    @jewulo 4 роки тому

    How is Optional_nontrivial able to use static_cast(this)->data.~Contained()? Is that SFINAE? Or is just the case that it doesn't matter at that level of the file because it is still a template? It can only generate an error if during template substitution it fails to compile? It feels like a dependency that breaks encapsulation to me.

    •  3 роки тому

      SFINAE works only at the signature.
      It doesn't matter because it's a template. When used, Contained should be complete.

  • @Elite7555
    @Elite7555 4 роки тому

    There are so many cases where you have to carefully analyse the generated assembly to understand which overload actually gets chosen.

  • @Bill-pz8qc
    @Bill-pz8qc 3 роки тому

    I like all your c++posts. But one thing I don't like, or say a suggestion, is that I have always to watch them on a big monitor. The font is way too small for my phone, even too small for my 12 inches ipad. I hope all your future post at least good for watching on ipad. Thankd

  • @johanneskalmbach5719
    @johanneskalmbach5719 4 роки тому

    Doesn't the operator= require a placement new in case the Optional is uninitialized?
    (If the machinery is there, it should also work for non-trivial classes to really illustrate the point.)

  • @gast128
    @gast128 4 роки тому

    Let's make the C++ language even more complex. Isn't it possible that the C++ compiler itself detects that a C++ destructor only does trivial stuff and can therefore be subject to optimizations?

  • @thetdltornike
    @thetdltornike 4 роки тому

    I thought this was going to be about ::operator delete and how compilers create two versions of destructors with virtuals

  • @ОлексійБовсуновкий

    Compare optimization provided by compilers like visual c++, gcc, clang

  • @lck5217
    @lck5217 4 роки тому

    if you have non trivially destructible type, then two destructors are valid for this type, how compiler choose one?

    • @Voy2378
      @Voy2378 4 роки тому +1

      nope, only one is valid, since requires is not satisfied.

  • @iiVEVO
    @iiVEVO 4 роки тому +3

    🔥

  •  3 роки тому +1

    Why the union?

  • @aDifferentJT
    @aDifferentJT 4 роки тому

    could this not be done with explicit specialisation using TSFINAE?

    •  3 роки тому

      Destructors take no arguments nor return any, and aren't templated.

  • @ashtum
    @ashtum 4 роки тому +4

    7:30
    The code in line 7 shouldn't work, should it?
    static_cast(this)->data.~Contained();
    The compiler doesn't know the identifier 'Contained' at that moment.

    • @MaceUA
      @MaceUA 4 роки тому +1

      (wrong answer removed)

    • @minirop
      @minirop 4 роки тому

      Are you thinking of "it misses typename to disambiguate"? Some have been removed because not necessary.

    • @von_nobody
      @von_nobody 4 роки тому +2

      @@MaceUA No this code is not correct, I could put there `~X` and it still not have error as this code it not used any where.
      Even more, I do not think that template arguments names have any effect on any thing outside given template scope:
      godbolt.org/z/aarc1M hard error when function i used.

    • @WouterStudioHD
      @WouterStudioHD 4 роки тому

      @@von_nobody You're not doing the same as Jason did. You're inheriting the other way around. In your case F should be the base class, not the derived.

    • @von_nobody
      @von_nobody 4 роки тому

      @@WouterStudioHD This should not matter, try doing this is godbolt and see results, overall I come to conclusion that destruction name could be named same as any other function, only in case of template scope C++ can replace template argument name with correct type.
      Result is that if destructor function name match class then code will compile, other wise you will have error.

  • @mapron1
    @mapron1 4 роки тому +3

    Andrey Alexandresku talk about conxtexpr if just came up to my mind. That could easily be written as
    if constexpr (someting) {
    ~Optional() {}
    }
    Sadly that is not a thing in C++. Hope Metaclasses/codegen will help a little bit.

    • @mapron1
      @mapron1 4 роки тому

      @Ziggi Mon Using *same* syntax - yeah ,totally a bad idea. Just a reminder we have no such a power yet.

    • @YourCRTube
      @YourCRTube 4 роки тому

      You are remembering an old version of constexpr if, when it was allowed to be used without introducing a scope. It had fearsome backlash from Stroustrup and few others.

  • @dennisrkb
    @dennisrkb 4 роки тому

    Great video, how come you don't need the default ctor to require default destructibility though?

    • @akj7
      @akj7 4 роки тому

      Why?

    • @dennisrkb
      @dennisrkb 4 роки тому

      @@akj7 So we only ever define one dtor. Isn't it an error to have 2 dtors defined?

    • @akj7
      @akj7 4 роки тому +1

      @@dennisrkb These are not two destructors. He used CRTP to inherit from a base class that has one and the only destructor.
      At compile time, C++ detects whether or not the template parameter is trivially destructible then inherits from the appropriate class, which has the destructor.
      All this seem like the class has multiple destructors but it doesn't. This is clear, because : template class MyClass {}; is not a class. It generates different classes (signatures) according to the template parameter. So Optional and Optional like shown in the video will generate 2 different class signatures. This is also proven by the fact that you can't treat one as the other in for example a copy constructor.
      Now to your main question:
      Imagine:
      class MyClass
      {
      int a;
      public:
      Myclass(int _a): a{_a} {}
      ~MyClass() = default;
      };
      MyClass is not default constructible but it is default destructible.

    • @dennisrkb
      @dennisrkb 4 роки тому

      @@akj7 thx for the elaborate answer. My confusion is around 11:53. Optional is a class with 2 dtors, one defaulted, the other conditionally defined. How does this compile if the condition is true and both definitions are present?

    • @akj7
      @akj7 4 роки тому +1

      @@dennisrkb Oh i see. Notice the 'constexpr' before the destructors? Notice the 'require'? Just like the explanation above, if the compiler notices that the template parameter is not trivially destructible, the first destructor is enabled (default destructors don't appear in the assembly output, meaning they are ignored), if not only the default constructor is available. All this is set at compile time.

  • @Max-ob8gq
    @Max-ob8gq 4 роки тому

    Great!

  • @鲍凯文
    @鲍凯文 3 роки тому

    Nice. Huge readability boost. Are there uses of this that'd come up other than wrapper/container types?

  • @mariogalindoq
    @mariogalindoq 4 роки тому

    Jason: thank you for another good video, however std::string seems not to be a good example because you can't have a union containing a non-static data member with a non-trivial special member function. Therefore, creating an Optional with string fails. Any comment?

    • @IsaacClancy
      @IsaacClancy 4 роки тому +2

      You can have a member with a special member function in a union since c++11. However, the special member function will be deleted on the union if you don't implement it yourself. I don't know how this would work for an anonymous union though, as you need a name to define a destructor.

    • @mariogalindoq
      @mariogalindoq 4 роки тому

      @@IsaacClancy That's true. However even having a union name, it would be very difficult to implement a destructor for std::string because it would be necessary that you know very intricate details about the std library implementation. I think that Jason can suggest another example. At the end, what he want to show, and for many has been show very well, do not depends on the std library. Anyway, the current example has the fault that if you implement it as it is, it will give you a core dumped, creating confusion for a student.

    • @IsaacClancy
      @IsaacClancy 4 роки тому

      @@mariogalindoq The string's destructor isn't deleted, only the union's so you could explicitly call the destructor of the string in the union's destructor. However, this doesn't actually help as the union doesn't know if it has an active member. Instead the union's destructor should be empty for types that aren't trivially destructible and defaulted for types that are (this can be done using the trick that Jason shows) and calling the destructor of the active member of the union (if any) should be left to optional's destructor.

  • @baardi2
    @baardi2 4 роки тому +4

    Not really multiple destructors though, since templated code is just a code generator, and every generated class only has one destructor

  • @danielrhouck
    @danielrhouck 4 роки тому

    It looks like you accidentally added this to the playlist twice.

  • @danielmilyutin9914
    @danielmilyutin9914 4 роки тому

    I expected something like destructors be qualified with && for temporary objects. Or maybe it is quite useless idea.

  • @pooya130
    @pooya130 4 роки тому +2

    What does "most constrained" mean?

    • @semirvrana8501
      @semirvrana8501 4 роки тому

      Imagine you have some iterator concepts
      concept forward_iterator = input_iterator && output_iterator
      concept bidirectional_iterator = forward_iterator && some_other_contraints
      concept random_access_iterator = bidirectional_iterator && some_other_constraints
      The lower concepts have all the constraints that the above ones have and more, thus they are more constrained. If you have different versions of an algorithm for each iterator concept and pass it an iterator from std::vector, then that iterator fulfills all three concepts above and the compiler will choose the version for random access iterator which is the most constrained. Hope that explains it...

    • @pooya130
      @pooya130 4 роки тому

      @@semirvrana8501 I'm OK with that. I am wondering about cases where constraints are not a subset of one another. What happens in that case?

    • @semirvrana8501
      @semirvrana8501 4 роки тому +1

      @@pooya130 In that case you will get a hard error due to ambiguous instantiations since the compiler can't figure out which concept is the more constrained one.

    • @pooya130
      @pooya130 4 роки тому

      @@semirvrana8501 That makes sense. Thanks!

  • @Tinfoilpain
    @Tinfoilpain 4 роки тому +1

    Cool

  • @rodneyperkins2952
    @rodneyperkins2952 4 роки тому +17

    Is C++ a real computer language, or is it a religion with its own priesthood and (obscure) theology?

    • @ZarosUltor
      @ZarosUltor 4 роки тому +12

      Why not both?

    • @sera_kath
      @sera_kath 3 роки тому

      "You can make a religion out of this"

  • @platin2148
    @platin2148 4 роки тому +1

    Crazy 😝

  • @viraatchandra8498
    @viraatchandra8498 2 роки тому +2

    this is amazing, but it makes me hate my life.

  • @technologicalwaste7612
    @technologicalwaste7612 4 роки тому

    I thought you said never use std::move :)

  • @nunogomes6011
    @nunogomes6011 4 роки тому

    The ads are horrible. The content is great, Jason, but the interruptions are unbearable.

  • @jamesmccoy8568
    @jamesmccoy8568 2 роки тому

    Seems like maybe this language is a little too complex

  • @jasoncole7711
    @jasoncole7711 4 роки тому

    Urgh, do you REALLY need to embody more than one advert in your videos?

  • @wenggay1463
    @wenggay1463 4 роки тому

    Hello.im interesting your content

  • @abelgerli
    @abelgerli 4 роки тому

    Multiple destructors wtf.
    Your where able to shoutdown s whole CPP system back in the days when you made the biggest fail of all.
    Doing stuff in a destructor that triggers a exception.
    Worst case scenario where this went really bad was when this destructor was called when a exception was already thrown especially when you use smart pointer.
    So an exception thrown when the stack is already unwinding and a object gets destroyed was the death of the whole application.

  • @Heisenberg2097
    @Heisenberg2097 2 роки тому

    This looks like the most ugly code I've seen in years. What only happened to good C++? With every new concept the language deteriorates. JMHO.

  • @voltenheim
    @voltenheim 4 роки тому

    I don't see the value in any of these videos.. you're teaching a syntax platform and the novices in your channel think that you're teaching programming.. tell them at least that learning C++ is nothing but learning paradigms designed by other human beings where if your novice viewer learn C (not C++) very well he will be able to author his own programming language instead because learning a programming language platform is not learning programming. This C++ 11/17/2x whatever is simply heading towards the wall in a very high speed. Also most of them think you're great because of your knowledge of C++ hahahaha tell them about your long career in C and Assembly!!!

  • @xnoreq
    @xnoreq 4 роки тому

    Oh god, everytime I think it cannot possibly get more horrible new C++ features prove me wrong.
    Why do I need to to the compiler's work? The compiler knows the type (int) and knows that the destructor of an int does nothing, so the if (initialized) body also does nothing which means the whole destructor does nothing. As it's not virtual, it should be classified as trivial.
    All of this should work automatically without the need to re-implement it every single time. Disgusting.

    • @broken_abi6973
      @broken_abi6973 4 роки тому

      but the compiler knows most of the time and deduces it. The problem here is that in the string case, the destructor needs to be user-defined.

    • @xnoreq
      @xnoreq 4 роки тому

      @@broken_abi6973 I'm not sure if you read my post.
      C++ templates can be thought of as copy&paste with search&replace in the type, so a is different from a.
      Whether the destructors is user-defined or default should be irrelevant, as I've explained, as the compiler can analyze and optimize both "implementations" (instantiations) separately.

    • @broken_abi6973
      @broken_abi6973 4 роки тому

      @@xnoreq ah i see. nevermind then

  • @lotusseries7
    @lotusseries7 4 роки тому

    C++ is such a dead language. Literally the only use case for it is if you have to use DirectX on Windows. Sorry C++, you aren't the only girl at the dance anymore. Thankfully there are so many other languages that are better designed. Sorry for all you DirectX guys out there.

    • @Volian0
      @Volian0 3 роки тому +1

      ok boomer xd

    • @keshkek
      @keshkek Рік тому

      and how would you solve this particular problem? firstly, there is an optional in the standard C++ library, the problem does not need to be solved. secondly, if you need your own optional and you don't need additional performance, you can not use anything from the video, and write a simple implementation. moreover, this is library code, not user code. thirdly, if you really need exactly this behavior, what will other languages, in particular object-oriented ones, give you? to create an interface, different classes, one implements a trivially destructible one, the other is non-trivial. it's terrible to create so many entities instead of such a simple and compact solution as C++ offers - in C++, the corresponding solution will be written faster. and no, it's not dead, find out