C++ Weekly - Ep 363 - A (Complete?) Guide To C++ Unions

Поделиться
HTML-код
  • Опубликовано: 13 окт 2024
  • ☟☟ Awesome T-Shirts! Sponsors! Books! ☟☟
    Upcoming Workshop: Applied constexpr: The Power of Compile-Time Resources, C++ Under The Sea, October 10, 2024
    ► cppunderthesea...
    Notes and info about episode: github.com/lef...
    Ep 188 - C++20's `constexpr` `new` Support - • C++ Weekly - Ep 188 - ...
    T-SHIRTS AVAILABLE!
    ► The best C++ T-Shirts anywhere! my-store-d16a2...
    WANT MORE JASON?
    ► My Training Classes: emptycrate.com/...
    ► Follow me on twitter: / lefticus
    SUPPORT THE CHANNEL
    ► Patreon: / lefticus
    ► Github Sponsors: github.com/spo...
    ► Paypal Donation: www.paypal.com...
    GET INVOLVED
    ► Video Idea List: github.com/lef...
    JASON'S BOOKS
    ► C++23 Best Practices
    Amazon Paperback: amzn.to/47MEAhj
    Leanpub Ebook: leanpub.com/cp...
    ► C++ Best Practices
    Amazon Paperback: amzn.to/3wpAU3Z
    Leanpub Ebook: leanpub.com/cp...
    JASON'S PUZZLE BOOKS
    ► Object Lifetime Puzzlers Book 1
    Amazon Paperback: amzn.to/3g6Ervj
    Leanpub Ebook: leanpub.com/ob...
    ► Object Lifetime Puzzlers Book 2
    Amazon Paperback: amzn.to/3whdUDU
    Leanpub Ebook: leanpub.com/ob...
    ► Object Lifetime Puzzlers Book 3
    Leanpub Ebook: leanpub.com/ob...
    ► Copy and Reference Puzzlers Book 1
    Amazon Paperback: amzn.to/3g7ZVb9
    Leanpub Ebook: leanpub.com/co...
    ► Copy and Reference Puzzlers Book 2
    Amazon Paperback: amzn.to/3X1LOIx
    Leanpub Ebook: leanpub.com/co...
    ► Copy and Reference Puzzlers Book 3
    Leanpub Ebook: leanpub.com/co...
    ► OpCode Puzzlers Book 1
    Amazon Paperback: amzn.to/3KCNJg6
    Leanpub Ebook: leanpub.com/op...
    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...
    ► C++ Best Practices Forkable Coding Standards - github.com/cpp...
    O'Reilly VIDEOS
    ► Inheritance and Polymorphism in C++ - www.oreilly.co...
    ► Learning C++ Best Practices - www.oreilly.co...

Комментарии • 51

  • @WilhelmDrake
    @WilhelmDrake 3 месяца назад +1

    Love the music selection @13:00! Thumbs up for the music alone!

  • @victotronics
    @victotronics Год назад +30

    You're funny. 14 of the 16 minute go to increasingly abstruse (but totally fascinating) stuff about unions, followed by the lesson that one shouldn't do this. Indeed: optional, variant, any, expected are much nicer.

    • @reverendragnarok
      @reverendragnarok Год назад +1

      Sometimes you're still stuck talking to C code and its unions tho...

    • @victotronics
      @victotronics Год назад +2

      @@reverendragnarok Sure, but those unions don't have constructors.....

    • @ikomatteo3177
      @ikomatteo3177 Год назад +7

      I learned a new word (abstruse)

    • @cppweekly
      @cppweekly  Год назад +20

      That's how I teach. I show you all the pitfalls of the common way code is written then show you the simple correct way. So now you believe me!

  • @Minty_Meeo
    @Minty_Meeo Год назад +5

    The dangerous thing about unions is how well the undefined use-cases for them work on any compiler.

  • @LPLeo1000
    @LPLeo1000 Год назад +3

    6:29 Sometimes I think, that C++ does not want you to program in it.

  • @ferdistapenhorst
    @ferdistapenhorst Год назад +6

    For someone like me not being a senior developer, I am more interested in learning the reasons when to use Unions. What problem do they solve and when is it good practice to use them? Maybe worth a Part 2 episode? 😊

    • @cppweekly
      @cppweekly  Год назад +5

      Short answer: when you want a space efficient way of storing an object that might be one of several different types.
      Generally speaking, today, you should use std::variant in those cases, not a raw union. As I showed, it's hard to get right.

    • @snajper4141
      @snajper4141 Год назад +2

      Unions and bit-fields are great combo that can replace use of bitwise operations.

  • @LeDabe
    @LeDabe Год назад +4

    I *think* that if you have a union of say, two structs with common initial sequence of object (same type), you are allowed to de reference values of this common sub sequence. Say, union U { struct A {int A_; } an_a; struct B {int B_; } a_b; }; U a_u; a_u.an_a.a; and a_u.a_b.b; is legal. This is the underlying principle of many C libraries, say when you manipulate addresses using the typical sockets API or Windows Bitmap header as an example. I would be appreciate input on the subject.

    • @ph1534
      @ph1534 Год назад +5

      From the standard (section 12.3):
      [ Note: One special guarantee is made in order to simplify the use of unions: If a standard-layout union
      contains several standard-layout structs that share a common initial sequence (12.2), and if a non-static data
      member of an object of this standard-layout union type is active and is one of the standard-layout structs, it
      is permitted to inspect the common initial sequence of any of the standard-layout struct members; see 12.2.
      - end note ]

  • @r75shell
    @r75shell Год назад +1

    Why not call constructor of string inside of constructor of union using : s(s) syntax?

  • @Kazooie
    @Kazooie Год назад +1

    When you call destructor "s.std::string::~string();" What is this function call exactly and how to read this? I thought you would call destructor for local variable with "s.~string()". What am I missing?

    • @frydac
      @frydac Год назад

      First time I've seen this syntax too, I assume it means the same, but specifying the whole type/signature of the function iso depending on the context/object it is called on. Not sure though.

    • @cppweekly
      @cppweekly  Год назад +2

      Yeah I'm just fully qualifying the name of the destructor. Nothing fancy. In that context I couldn't get it to compile without fully qualifying it. I'm not 100% sure why.

  • @__hannibaalbarca__
    @__hannibaalbarca__ Год назад

    You gave me idea to make struct represente Union and so we can convert bits from type to other type without making a Union.
    I always watching you in Cppcon and other

  • @PaulMetalhero
    @PaulMetalhero Год назад +2

    Jason, raycasting engine tutorial in modern C++, please!

  • @milasudril
    @milasudril Год назад +3

    Shouln't you wait setting is_active until after construct_at, in case of an exception

    • @cppweekly
      @cppweekly  Год назад +2

      Probably. But I'm also pretty sure I told you to not use code like this :D

  • @VioletGiraffe
    @VioletGiraffe Год назад

    Anonymous unions used to be cause a lot of GCC / clang warnings, is that no longer the case?

  • @fcolecumberri
    @fcolecumberri Год назад +1

    Love the last point: just use STL.

  • @greyfade
    @greyfade Год назад

    I have to wonder why, when literally everyone who has ever used unions uses them with UB, the C and C++ committees don't actually *fix* them.

    • @cppweekly
      @cppweekly  Год назад +1

      You can use them without UB in C, and changing them would break backward compatibility.

  • @keshkek
    @keshkek Год назад +1

    It's nice that we can't have UB in constant expression context due to C++ standard expr.const. If we can write the code that will executed in constexpr context we'll have no UB in this code. I'm starting to think it's safer than situation in some other language in which UB can take place not only in some blocks but because of these blocks (somewhere safe)

  • @TheOneTrueMaNicXs
    @TheOneTrueMaNicXs Год назад +1

    What I want to know is why lParam and wParam in WndProc aren't unions.

    • @Webfra14
      @Webfra14 Год назад

      Unions are evil, avoid at all cost!

    • @WndSks
      @WndSks Год назад

      union AllMessageParametersThatExist {...}; History makes it impossible. In 16-bit Windows, some messages actually put two pointer sized items in LPARAM.

  • @tilmanrotationalinvariant2257
    @tilmanrotationalinvariant2257 Год назад

    So why did he have to manually call the destructor? Shouldn't it be called either way?

    • @StuartDootson
      @StuartDootson Год назад +3

      Because constructors and destructors aren’t called for objects in unions, as the compiler has no general way of knowing if the object is the active member of the union.

  • @progammler
    @progammler Год назад

    Is using unions to convert types functionally equivalent to reinterpret casts on local variables? In HLS we make local copies and use unions to convert types because reinterpret casts are unsupported. I understand that reinterpreting pointers with fixed hardware is not trivial but I don't understand why the compiler can translate conversions of local variables with unions bot not with reinterpret casts.

    • @cppweekly
      @cppweekly  Год назад +1

      Yes, it is morally equivalent to a reinterpret_cast.
      As I attempted to very clearly state in the episode, you *can not* access the non-active member of a union without invoking UB.

  • @tialaramex
    @tialaramex Год назад

    I didn't know before that C++ unions are allowed to have member functions - thanks Jason.

  • @broken_abi6973
    @broken_abi6973 Год назад +1

    If only we had variants/safe unions with the interface of unions...

  • @nirajandata
    @nirajandata Год назад

    wait a minute, this video was literally posted 1 minutes ago but the comment below is commented 1 day ago, how is this even possible?

    • @nirajandata
      @nirajandata Год назад +2

      never mind, i got it

    • @Webfra14
      @Webfra14 Год назад +1

      @@nirajandata Welcome to the time traveling community!

    • @marksilverman
      @marksilverman Год назад +19

      That comment must have been created at compile-time.

  • @RedOrav
    @RedOrav Год назад

    One thing I don't understand is this idea of the "active member of a union". At runtime I don't think this concept exists, if you modify the int and you access the float, you get the float that is represented by the same bits you set on the int, and that's totally normal behavior and there's nothing UB about it as far as I know. How come at compile time these rules change? Feels like union at compile time just cannot really be sensibly used.

    • @StuartDootson
      @StuartDootson Год назад +4

      That is absolutely undefined behaviour in C++, and just because it happens to work with your current compiler is no guarantee that it will work in the future. And if the compiler can tell that you’re reading the inactive member, it is quite at liberty to alter that to whatever it wants if it thinks that’ll make more optimised code.
      If you want to bitwise transfer a float to int or vice versa, use memcpy or std::bit_cast in C++20

    • @sirhenrystalwart8303
      @sirhenrystalwart8303 Год назад +3

      @@StuartDootson Yeah this is my big gripe with c++. That most compilers currently behave "reasonably" indicates to me that reasonableness is possible. But the standard insists on calling this undefined behavior which is really frustrating. Many examples like this and they don't seem to be interested in fixing it.

    • @StuartDootson
      @StuartDootson Год назад

      @@sirhenrystalwart8303 it’s not just c++ - it’s c as well. C could have standardised 2s complement arithmetic (which is the de-facto hardware standard for signed integer representation), but for the sake of portability and backwards compatibility, they chose not to define how signed integers work when they overflow, and chose instead to effectively say ‘just don’t do it’, and haven’t changed that stance even though the computing landscape has changed significantly since C was first developed.
      See, back then, there were a few 1s complement computers. You couldn’t rely on ASCII, as IBM mainframes used EBCDIC. It made sense for C not to define behaviour in those areas where the platforms fundamentally differed, so as to allow efficient code generation on all platforms. Does that make sense now? Maybe not… unless you still run a PDP-1 or CDC-6600 and you need a C compiler!

    • @chewey
      @chewey Год назад +2

      What you're describing is extremely common in low level/embedded programming, and it's definitely the nicest solution in a lot of cases, but it's still UB. In my experience, you aren't going to convince people to do something more cumbersome on the off-chance a future compiler disregards common practice.

    • @frydac
      @frydac Год назад

      yeah, I do think many c and/or cpp codebases depend on UB in some way. I think one pragmatic way to deal with it is to have an automated test that confirms the 'reasonable compiler behavior' the codebase depends on, at least if you know that you are depending on UB. Use UB sanitizers to minimize the issue as much as possible (in combo with fuzz testing if possible). Most of embedded code can be in a x86/ARM compilable/runable lib that can easily be tested.

  • @sprytnychomik
    @sprytnychomik Год назад

    Wow. Coding in modern C++ is like playing classic Tomb Raider games.

  • @LeDabe
    @LeDabe Год назад

    if you use, std::construct_at,/destroy_at, you are not far of using std::address_of to, for completeness' sake.

    • @cppweekly
      @cppweekly  Год назад +1

      The short answer is that you use a union-like type when a value can be 1 of several different types, and you want to store that in a memory-efficient way.
      You should prefer std::variant in those cases today. Use the tools the standard library gives you.