ChatGPT解决这个技术问题 Extra ChatGPT

What is the purpose of std::launder?

P0137 introduces the function template std::launder and makes many, many changes to the standard in the sections concerning unions, lifetime, and pointers.

What is the problem this paper is solving? What are the changes to the language that I have to be aware of? And what are we laundering?

Are you asking about the paper itself or about std::launder? std::launder is used to "obtain a pointer to an object created in storage occupied by an existing object of the same type, even if it has const or reference members."
useful link on the subject. Also this question stackoverflow.com/questions/27003727/…
This has now been released in VC2017 in version 15.7.0
According to the std, pointers are trivial types so launder doesn't do anything. ;)

C
Community

std::launder is aptly named, though only if you know what it's for. It performs memory laundering.

Consider the example in the paper:

struct X { const int n; };
union U { X x; float f; };
...

U u = {{ 1 }};

That statement performs aggregate initialization, initializing the first member of U with {1}.

Because n is a const variable, the compiler is free to assume that u.x.n shall always be 1.

So what happens if we do this:

X *p = new (&u.x) X {2};

Because X is trivial, we need not destroy the old object before creating a new one in its place, so this is perfectly legal code. The new object will have its n member be 2.

So tell me... what will u.x.n return?

The obvious answer will be 2. But that's wrong, because the compiler is allowed to assume that a truly const variable (not merely a const&, but an object variable declared const) will never change. But we just changed it.

[basic.life]/8 spells out the circumstances when it is OK to access the newly created object through variables/pointers/references to the old one. And having a const member is one of the disqualifying factors.

So... how can we talk about u.x.n properly?

We have to launder our memory:

assert(*std::launder(&u.x.n) == 2); //Will be true.

Money laundering is used to prevent people from tracing where you got your money from. Memory laundering is used to prevent the compiler from tracing where you got your object from, thus forcing it to avoid any optimizations that may no longer apply.

Another of the disqualifying factors is if you change the type of the object. std::launder can help here too:

alignas(int) char data[sizeof(int)];
new(&data) int;
int *p = std::launder(reinterpret_cast<int*>(&data));

[basic.life]/8 tells us that, if you allocate a new object in the storage of the old one, you cannot access the new object through pointers to the old. launder allows us to side-step that.


So is my tl;dr correct: "laundering is basically for non-UB type punning"?
Could you explain why this is true? "Because n is a const variable, the compiler is free to assume that u.x.n shall always be 1." Where in the standard does it say that? I ask because the very problem you pointed out would seem to imply to me that it is false in the first place. It should only be true under the as-if rule, which fails here. What am I missing?
How much can we sidestep that aliasing rule? Like template <class T, class U> T* alias_cast(U* ptr) { return std::launder(reinterpret_cast<T*>(ptr)); } How UB is that?
@Barry Very; if there's no objects of type T located at the address ptr represents, then you break launder's precondition, so there's no point talking about the outcome.
@NicolBolas A good optimising compiler will optimise your correct solution of memcpy into an in-place reinterpretation on supported (i.e. lax alignment) platforms anyway.
e
einpoklum

std::launder is a mis-nomer. This function performs the opposite of laundering: It soils the pointed-to memory, to remove any expectation the compiler might have regarding the pointed-to value. It precludes any compiler optimizations based on such expectations.

Thus in @NicolBolas' answer, the compiler might be assuming that some memory holds some constant value; or is uninitialized. You're telling the compiler: "That place is (now) soiled, don't make that assumption".

If you're wondering why the compiler would always stick to its naive expectations in the first place, and would need to you to conspicuously soil things for it - you might want to read this discussion:

Why introduce `std::launder` rather than have the compiler take care of it?

... which led me to this view of what std::launder means.


I dunno, seems to perform exactly laundering to me: it's removing the provenance of the pointer so that it's clean, and needs to be (re-)read. I don't know what "soiling" means in this context.
@Barry: Memory into which anybody might have thrown/written stuff is dirty, not clean. If I give you an article of clothing without provenance information - who knows where it's been? You would definitely put it in the dirty laundry hamper to be washed.
I agree that std::launder is named exactly backwards if it's meant to refer to money laundering, but I don't think you should say that it soils the memory. Dirty money is dirty whether "laundered" or not, but the laundering makes people wrongly assume that it's clean. Dirty memory is dirty whether std::laundered or not, but the laundering makes the compiler stop wrongly assuming that it's clean.
Re: "That place is now soiled, don't make that assumption" - Or, "that place is soiled, please std::launder it"
@benrg: Money that has been laundered is clean. If it can be proven that someone stole $7,500, laundered it, and then used the money to buy a used car for $7,500, the government may seize the car, but unless the seller of the car was an accessory to the theft or money laundering, the seller would be entitled to keep the $7,500 from the sale.
F
F.v.S.

I think there are two purposes of std::launder.

A barrier for constant folding/propagation, including devirtualization. A barrier for fine-grained object-structure-based alias analysis.

Barrier for overaggressive constant folding/propagation (abandoned)

Historically, the C++ standard allowed compilers to assume that the value of a const-qualified or reference non-static data member obtained in some ways to be immutable, even if its containing object is non-const and may be reused by placement new.

In C++17/P0137R1, std::launder is introduced as a functionality that disables the aforementioned (mis-)optimization (CWG 1776), which is needed for std::optional. And as discussed in P0532R0, portable implementations of std::vector and std::deque may also need std::launder, even if they are C++98 components.

Fortunately, such (mis-)optimization is forbidden by RU007 (included in P1971R0 and C++20). AFAIK there's no compiler performing this (mis-)optimization.

Barrier for devirtualization

A virtual table pointer (vptr) can be considered constant during the lifetime of its containing polymorphic object, which is needed for devirtualization. Given that vptr is not non-static data member, compilers is still allowed to perform devirtualization based on the assumption that the vptr is not changed (i.e., either the object is still in its lifetime, or it is reused by a new object of the same dynamic type) in some cases.

For some unusual uses that replace a polymorphic object with a new object of different dynamic type (shown here), std::launder is needed as a barrier for devirtualization.

IIUC Clang implemented std::launder (__builtin_launder) with these semantics (LLVM-D40218).

Barrier for object-structure-based alias analysis

P0137R1 also changes the C++ object model by introducing pointer-interconvertibility. IIUC such change enables some "object-structure-based alias analysis" proposed in N4303.

As a result, P0137R1 makes the direct use of dereferencing a reinterpret_cast'd pointer from an unsigned char [N] array undefined, even if the array is providing storage for another object of correct type. And then std::launder is needed for access to the nested object.

This kind of alias analysis seems overaggressive and may break many useful code bases. AFAIK it's currently not implemented by any compiler.

Relation to type-based alias analysis/strict aliasing

IIUC std::launder and type-based alias analysis/strict aliasing are unrelated. std::launder requires that an living object of correct type to be at the provided address.

However, it seems that they are accidently made related in Clang (LLVM-D47607).