Open
Description
From https://stackoverflow.com/q/66755218
[tab:cpp17.allocator] has the following example added by p0593r6:
When reusing storage denoted by some pointer value
p
,launder(reinterpret_cast<T*>(new (p) byte[n * sizeof(T)]))
can be used to implicitly create a suitable array object and obtain a pointer to it.
If T
is not an implicit-lifetime type, then, even if an object of type T[m ≤ n]
is created, the lifetimes of the array elements are not started, which means launder
's preconditions are violated:
Preconditions:
p
represents the addressA
of a byte in memory. An objectX
that is within its lifetime and whose type is similar toT
is located at the addressA
.
Metadata
Metadata
Assignees
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
theundergroundsorcerer commentedon Mar 23, 2021
The usage is indeed incorrect. The valid cast is
launder(reinterpret_cast<T(*)[n]>(new (p) byte[n * sizeof(T)]))
(May ben
is not necessary) I believe. The problem is that value of the array (as a pointer) is valid pointer for purposes of arithmetic and casting to it and laundering it should be allowed as long as I do not de-reference it, (without constructing an object first).xmh0511 commentedon Jan 17, 2022
Incidentally, the standard never specifies which implicitly created objects whose addresses are the address of the start of the region of storage.
jwakely commentedon Jan 17, 2022
That's intentional. The right one is selected, by magic, if that would make the program correct.
None of this is editorial.
xmh0511 commentedon Jan 17, 2022
So, we cannot plainly determine whether a program is well-formed. For instance
No one can say the return value of
malloc
can be the address of objectX
. AlthoughX
is implicitly-lifetime type, however, we don't know whether that object's address is the address of the start of the region of storage. I.E., only the implementor of the functionmalloc
knows that.jwakely commentedon Jan 17, 2022
No, that's not what the wording means. There is no "implementor of the function
malloc
," because it's part of the implementation and so behaves as the standard says (not as some developer says). The standard says that if beginning the lifetime of an object of typeX
at that location would make the program have defined behaviour, then that's what happens. So the answer to the "is well-formed?" question is yes. By definition. If creating anX
there makes it well-formed, then that's what happens, and so it's well-formed.languagelawyer commentedon Jan 17, 2022
«well-formed» should not be confused with «well-defined»
jwakely commentedon Jan 17, 2022
Yes, and
ptr->x = 0
is always well-formed whenptr
has typeX*
. It's also well-defined becausemalloc
implicitly creates objects of the type needed to make it have defined behaviour.xmh0511 commentedon Jan 18, 2022
@jwakely However, the standard/implementation never says that the object of type
X
would be located at the start address of that region. This is the confusion here. Actually, [intro.object] p11 imposes two requirements:we can say the object of type
X
satisfies the first bullet since the standard defines the "implicitly-lifetime type", hencemalloc
implicitly creates the object with typeX
. However, the standard never says these objects' addresses are what(i.e., whether their addresses satisfy the second bullet). Based on this logic, we cannot determine whetherptr->x = 0;
is well-defined or not.xmh0511 commentedon Jan 18, 2022
Cite the quotes of the definition of
malloc
in CThe standard only specifies that an object of type
T
and the member subobjectT::i
is pointer-interconvertible, hence they have the same address. However, neither in C++ nor C standard, they ever specify, through any provision, that the addresses of a typeT
object and its subobjectT::i
are identical to the address of the start of the region of the storage that is allocated by themalloc
function. when we say the above example is well-defined is just based on the premise that these objects whose addresses are the address of the start of the allocated space. However, this premise has no formal definition to prove in the current standard.frederick-vs-ja commentedon Jul 20, 2022
P2590R0 addressed this issue by replace the use with
start_lifetime_as_array<T>(p, n)
, but this change was removed in later revisions.jensmaurer commentedon Mar 12, 2023
Yes, because that's a situation where we're not intending to re-use the existing bytes for the to-be object representation. As-is, the optimizer can apply dead-store elimination for any store to the affected region.
frederick-vs-ja commentedon Mar 13, 2023
start_lifetime_as_array<T>(new (p) byte[n * sizeof(T)], n)
seemingly works as intended. Although there's an open issue CWG1997 indicating that it may be unclear whether the new-expression renders the contents in the storage indeterminate.jensmaurer commentedon Mar 13, 2023
That core issue far predates
std::start_lifetime_as
. I think the goal of CWG1997 is (and continues to be) to clarify that placement-new without initialization also yields an object with indeterminate value.13 remaining items