Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Do I need `std::launder` when working with unions?

Consider the following code:

#include <iostream>
#include <string>

union U
{
    std::string s;

    U()
    {
        new(&s) std::string;
    }

    void Set(std::string new_s)
    {
        s.~basic_string();
        new(&s) std::string(std::move(new_s));
    }

    ~U()
    {
        s.~basic_string();
    }
};

int main()
{
    U u;
    u.Set("foo");
    std::cout << u.s << '\n'; // Do I need `std::launder` here?
}

I know that if I used a byte array instead of a union, I’d have to std::launder(&s) to access the string. But do I need it here?

The common sense answer seems to be "no", but then where does the standard bless unions to not need std::launder?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

The member access expression u.s is simply defined to result in a lvalue to the s member subobject of the union. (That is true even if u.s is not in its lifetime, i.e. not active.) See [expr.ref]/6.2.

So there can’t be any need to launder. You already have a glvalue to the object you want.

Even if you start from a pointer to the union object instead and attempt to use a pointer cast approach, more similar to the byte array approach, then it will still work without std::launder, e.g.:

std::cout << *reinterpret_cast<std::string*>(&u) << '\n';

That is because a union object is pointer-interconvertible with any of its member subobjects per [basic.compound]/4.2, meaning that the reinterpret_cast will produce a pointer to the member subobject immediately per [expr.static.cast]/14.

std::launder is required if the result of a cast still points to the original object due to lack of pointer-interconvertibility between the source and the target object.


And also, per [basic.object]/2 together with [basic.life]/8 the objects you create with new will become member subobjects of the union and s in u will designate the most recent such subobject, because they satisfy all of the requirements mentioned in those paragraphs (in particular exact overlap with s and exact type match with s).

Therefore s.u will also not accidentally refer to the previous std::string subobject that has been replaced with a later new.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading