Is using placement new with variable on the stack is correct? - c++

Let's take a look to this code:
A a(123);
new(&a) A(124);
Test says that in this case when program is shutting down destructor ~A() will called once. So if in A we has some pointers as fields we will get memory leak.
A a(123);
a.~A();
new(&a) A(124);
Here all will be correct. But according to standard using object after destructor calling is undefined behaviour(though mostly compilers provide behaviour without some troubles).
Can I take an address of object which destructor has been called?
Is calling placement new on stack variable is correct operation?

Can I take an address of object which destructor has been called?
[edited:]
Such an address is valid so if you have a pointer to it, it is valid.
Can you take its address after the fact, I am not fully sure.
basic.life.6 […] After the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object was located may be used but only in limited ways. […] Such a pointer refers to allocated storage, and using the pointer as if the pointer were of type void* is well-defined.
Check out the full text for the all the restrictions, but use in placement-new is allowed.
As for the comments, I would argue both your samples are correct within the scope you showed.
A a(123);
new(&a) A(124);
I would argue this, given what we know in the sample, is correct.
Ending the lifetime of an object by re-using its storage is valid as per basic.life.5. Only condition is the program does not depend on side effects produced by the destructor - to be on the safe side I would only do that on trivially destructible types. Otherwise you need an explicit destructor call, like you did there:
A a(123);
a.~A();
new(&a) A(124);
I do not see any rule preventing that. The standard even explicitly mentions when such a construct is invalid:
If a program:
ends the lifetime of an object of type T with static, thread, or
automatic storage duration
and another object of the original type does not occupy that same
storage location when the implicit destructor call takes place,
the behavior of the program is undefined.
(formatting as bullet points mine)
Though not a definite proof, this passage suggests that in other cases, the behavior is defined. I cannot think of another rule that his would violate.
(Do note I use C++20 version of the standard)

Yes, this usage is valid. The storage for an object is independent from the object's lifetime.
By calling the destructor, you are ending the object's lifetime, but that does not mean that the storage is released.
According to the standard, that storage may be reused or released. And what you do is reusing it.
This exact case is well-defined in the standard in basic.life#8
Keep in mind that, because 'a' is a variable with automatic storage, at the end of the scope the destructor for that variable's type will be invoked.

Related

Is it really UB to access an object whose user-defined destructor has started, but not finished?

The question arose due to a discussion on Reddit, where a user told me, citing the standard's rules on object lifetime:
I'm pretty certain that it is technically UB to access an object, while it is being destructed.
I rely on this, for example, with classes that manage a background thread; I have their destructors signal the thread to exit and wait until it does, and that thread may access the object. Do I need to refactor my code?
No, it's well-defined.
If it were UB to access the object at all while its destructor is executing, that destructor would itself not be able to do anything with its own object. 😜
During execution of your destructor:
bases have not been destructed yet
the "current" object itself has not been destructed yet (and neither have its members)
some resources may have been released, if you did so in your destructor already
derived subobjects have been destructed
virtual function calls will safely refer to the "current" object, rather than these now-dead derived subobjects
dynamic_cast and typeid will do the same
you must not do any of these using a Derived*, though! Via a Base* or Current* is fine
Most of these rules are covered by [class.cdtor].
Though the object's lifetime does technically end with the beginning of the destructor "call", at this point you're in a kind of purgatory where [class.cdtor] takes over with the rules listed above:
[basic.life/7]: [..] after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. [..]
It's potentially an error-prone and confusing pattern, but it's not inherently incorrect. For your particular use case I'd even call it reasonably conventional.

Why isn't it undefined behaviour to destroy an object that was overwritten by placement new?

I'm trying to figure out whether the following is undefined behaviour. I have a feeling it's not UB, but my reading of the standard makes it look like it is UB:
#include <iostream>
struct A {
A() { std::cout << "1"; }
~A() { std::cout << "2"; }
};
int main() {
A a;
new (&a) A;
}
Quoting the C++11 standard:
basic.life¶4 says "A program may end the lifetime of any object by reusing the storage which the object occupies"
So after new (&a) A, the original A object has ended its lifetime.
class.dtor¶11.3 says that "Destructors are invoked implicitly for constructed objects with automatic storage duration ([basic.stc.auto]) when the block in which an object is created exits ([stmt.dcl])"
So the destructor for the original A object is invoked implicitly when main exits.
class.dtor¶15 says "the behavior is undefined if the destructor is invoked for an object whose lifetime has ended ([basic.life])."
So this is undefined behaviour, since the original A no longer exists (even if the new a now exists in the same storage).
The question is whether the destructor for the original A is called, or whether the destructor for the object currently named a is called.
I am aware of basic.life¶7, which says that the name a refers to the new object after the placement new. But class.dtor¶11.3 explicitly says that it's the destructor of the object which exits scope which is called, not the destructor of the object referred to by a name that exits scope.
Am I misreading the standard, or is this actually undefined behaviour?
Edit: Several people have told me not to do this. To clarify, I'm definitely not planning on doing this in production code! This is for a CppQuiz question, which is about corner cases rather than best practices.
You're misreading it.
"Destructors are invoked implicitly for constructed objects" … meaning those that exist and their existence has gone as far as complete construction. Although arguably not entirely spelled out, the original A does not meet this criterion as it is no longer "constructed": it does not exist at all! Only the new/replacement object is automatically destructed, then, at the end of main, as you'd expect.
Otherwise, this form of placement new would be pretty dangerous and of debatable value in the language. However, it's worth pointing out that re-using an actual A in this manner is a bit strange and unusual, if for no other reason than it leads to just this sort of question. Typically you'd placement-new into some bland buffer (like a char[N] or some aligned storage) and then later invoke the destructor yourself too.
Something resembling your example may actually be found at basic.life¶8 — it's UB, but only because someone constructed a T on top of an B; the wording suggests pretty clearly that this is the only problem with the code.
But here's the clincher:
The properties ascribed to objects throughout this International Standard apply for a given object only during its lifetime. [..] [basic.life¶3]
Am I misreading the standard, or is this actually undefined behaviour?
None of those. The standard is not unclear but it could be clearer. The intent though is that the new object's destructor is called, as implied in [basic.life]p9.
[class.dtor]p12 isn't very accurate. I asked Core about it and Mike Miller (a very senior member) said:
I wouldn't say that it's a contradiction [[class.dtor]p12 vs [basic.life]p9], but clarification is certainly needed. The destructor description was written slightly naively, without taking into consideration that the original object occupying a bit of automatic storage might have been replaced by a different object occupying that same bit of automatic storage, but the intent was that if a constructor was invoked on that bit of automatic storage to create an object therein - i.e., if control flowed through that declaration - then the destructor will be invoked for the object presumed to occupy that bit of automatic storage when the block is exited - even it it's not the "same" object that was created by the constructor invocation.
I'll update this answer with the CWG issue as soon as it is published. So, your code does not have UB.
Too long for a comment.
Lightness' answer is correct and his link is the proper reference.
But let's examine terminology more precisely. There is
"Storage duration", concerning memory.
"Lifetime", concerning objects.
"Scope", concerning names.
For automatic variables all three coincide, which is why we often do not clearly distinguish: A "variable goes out of scope". That is: The name goes out of scope; if it is an object with automatic storage duration, the destructor is called, ending the lifetime of the named object; and finally the memory is released.
In your example only name scope and storage duration coincide — at any point during its existence the name a refers to valid memory — , while object lifetime is split between two distinct objects at the same memory location and with the same name a.
And no, I think you cannot understand "constructed" in 11.3 as "fully constructed and not destroyed" because the dtor will be called again (wrongly) if the object's lifetime was ended prematurely by a preceding explicit destructor call.
In fact, that's one of the concerns with the concept of memory re-use: If construction of the new object fails with an exception the scope will be left and a destructor call will be attempted on an incomplete object, or on the old object which was deleted already.
I suppose you can imagine the automatically allocated, typed memory marked with a tag "to be destroyed" which is evaluated when the stack is unwound. The C++ runtime does not really track individual objects or their state beyond this simple concept. Since variable names are basically constant addresses it is convenient to think of "the name going out of scope" triggering the destructor call on the named object of the supposed type supposedly present at that location. If one of these suppositions is wrong all bets are off.
Imagine using placement new to create a struct B to the storage where the A a objects lives. In the end of the scope, the destructor of the struct A will be called (because the variable a of type A goes out of scope), even if an object of type B is in reallty living there right now.
As already cited:
"If a program ends the lifetime of an object of type T with static
([basic.stc.static]), thread ([basic.stc.thread]), or automatic
([basic.stc.auto]) storage duration and if T has a non-trivial
destructor,39 the program must ensure that an object of the original
type occupies that same storage location when the implicit destructor
call takes place;"
So after putting B into the a storage, you need to destroy B and put an A there again, to not violate the rule above. This somehow not apply here directly, because you are putting an A to an A, but it shows the behavior. It shows, that this thinking is wrong:
So the destructor for the original A object is invoked implicitly when
main exits.
There is no "original" object any longer. There is just an object currently alive in the storage of a. And thats it. And on the data currently sitting in a, a function is called, namely the destructor of A. Thats what the program compiles to. If it would magically kept track of all "original" objects you would somehow have a dynamic runtime behavior.
Additionally:
A program may end the lifetime of any object by reusing the storage
which the object occupies or by explicitly calling the destructor for
an object of a class type with a non-trivial destructor. For an object
of a class type with a non-trivial destructor, the program is not
required to call the destructor explicitly before the storage which
the object occupies is reused or released; however, if there is no
explicit call to the destructor or if a delete-expression
([expr.delete]) is not used to release the storage, the destructor
shall not be implicitly called and any program that depends on the
side effects produced by the destructor has undefined behavior.
Since the destructor of A is not trivial and has side effects, (i think) its undefined behavior. For build in types, this does not apply (hence you can use a char buffer as object buffer without reconstructing the chars back into the buffer after using it) since they have a trivial (no-op) destructor.

Is replacing `this` with a different type allowed?

In the comments and answers to this question:
Virtual function compiler optimization c++
it is argued that a virtual function call in a loop cannot be devirtualized, because the virtual function might replace this by another object using placement new, e.g.:
void A::foo() { // virtual
static_assert(sizeof(A) == sizeof(Derived));
new(this) Derived;
}
The example is from a LLVM blog article about devirtualization
Now my question is: is that allowed by the standard?
I could find this on cppreference about storage reuse: (emphasis mine)
A program is not required to call the destructor of an object to end its lifetime if the object is trivially-destructible or if the program does not rely on the side effects of the destructor. However, if a program ends the lifetime of an non-trivial object, it must ensure that a new object of the same type is constructed in-place (e.g. via placement new) before the destructor may be called implicitly
If the new object must have the same type, it must have the same virtual functions. So it is not possible to have a different virtual function, and thus, devirtualization is acceptable.
Or do I misunderstand something?
The quote you provided says:
If a program ends the lifetime of an non-trivial object, it must ensure that a new object of the same type is constructed in-place (e.g. via placement new) before the destructor may be called implicitly
The intent of this statement relates to something a bit different to what you are doing. The statement is meant to say that when you destroy an object without destroying its name, something still refers to that storage with the original type, o you need to construct a new object there so that when the implicit destruction occurs, there is a valid object to destroy. This is relevant for example if you have an automatic ("stack") variable, and you call its destructor--you need to construct a new instance there before the destructor is called when the variable goes out of scope.
The statement as a whole, and its "of the same type" clause in particular, has no bearing on the topic you're discussing, which is whether you are allowed to construct a different polymorphic type having the same storage requirements in place of an old one. I don't know of any reason why you shouldn't be allowed to do that.
Now, that being said, the question you linked to is doing something different: it is calling a function using implicit this in a loop, and the question is whether the compiler could assume that the vptr for this will not change in that loop. I believe the compiler could (and clang -fstrict-vtable-pointers does) assume this, because this is only valid if the type is the same after the placement new.
So while the quotes from the standard you have provided are not relevant to this issue, the end result is that it does seem possible for an optimizer to devirtualize function calls made in a loop under the assumption that the type of *this (or its vptr) cannot change. The type of an object stored at an address (and its vptr) can change, but if it does, the old this is no longer valid.
It appears that you intend to use the new object using handles (pointers, references, or the original variable name) that existed prior to its recreation. That's allowed only if the instance type is not changed, plus some other conditions excluding const objects and sub-objects:
From [basic.life]:
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:
the storage for the new object exactly overlays the storage location which the original object occupied,
and
the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and
the original object was a most derived object of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).
Your quote from the Standard is merely a consequence of this one.
Your proposed "devirtualization counter-example" does not meet these requirements, therefore all attempts to access the object after it is replaced will cause undefined behavior.
The blog post even pointed this out, in the very next sentence after the example code you looked at.

What does it mean for an object to exist in C++?

[class.dtor]/15 reads, emphasis mine:
Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended (3.8).
However, as far as I can tell, this is the only reference in the standard to an object "existing." This also seems to contrast with [basic.life], which is more specific:
The lifetime of an object of type T ends when:
if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
the storage which the object occupies is reused or released.
We have two different wordings here: "the lifetime of an object ends" and "the object no longer exists," the former only happens with a non-trivial destructor and the latter happens with any destructor. What is the significance of the difference? What is the implication of an object no longer existing?
The quoted wording would seem to imply that a compiler could correctly insert code that returns the memory associated with an object to the heap at the beginning of its destructor. But doing that would eliminate the ability of an object to reference its own members during destruction, which is required if an object is to be able to destroy itself.
So I think the quoted wording is broken and should be fixed.
Concerning what "lifetime" and "existence" mean, I propose that there are some different contexts, in which they mean different things:
Within the context of construction, lifetime and existence begin when a constructor begins. Outside that context, they begin when a constructor ends.
Within the context of destruction, lifetime and existence end when a destructor ends. Outside that context, they end when destruction begins.
So an object may refer to its own members during construction, and potentially pass itself to functions of other objects, which may refer to the object and its members, and so on. But in general, objects (instances of classes) may not be referenced (without producing undefined behavior) until after one of their constructors has finished.
And an object's destructor may refer to its own members and call functions of other (existing) objects, which may refer to the object being destroyed and/or its members. But in general, an object may not be referenced after its destructor has started.
This sort of multi-contextual definition is what makes the most sense to me, but I can see arguments being made that an object should be considered to be alive from the moment memory is allocated for it to the moment that memory is released, and I would say memory for a shallow copy should be allocated for an object when one of its constructors starts, and released when its destructor ends.

When is placement new well defined, and what happens to an existing type when calling placement new?

I've seen some examples of placement new, and am a little confused as to what's happening internally with the various types.
A simple example:
using my_type = std::string;
using buffer_type = char;
buffer_type buffer[1000];
my_type* p{ new (buffer) my_type() };
p->~my_type();
From what I understand, this is valid, though I'm concerned about what happens to the char array of buffer[]. It seems like this is ok to do as long as I don't access the variable buffer in any form after creating a new object in place on its memory.
For the sake of keeping this simple, I'm not concerned about anything to do with proper alignment here, or any other topics of what can go wrong when calling placement new other than: what happens to the original type? Could I use another type such as buffer_type = int to achieve a similar effect (ignoring the possibility of how much memory that will actually take)? Is any POD safe as buffer_type? How about non-POD types? Do I have to tell the compiler that the char array is no longer valid in some way? Are there restrictions on what my_type could be or not be here?
Does this code do what I expect it to, is it well defined, and are there any minor modifications that would either keep this as well defined or break it into undefined behaviour?
what happens to the original type?
You mean the original object? It get's destroyed, that is, its lifetime ends. The lifetime of the array object of type buffer_type [1000] ends as soon as you reuse its storage.
A program may end the lifetime of any object by reusing the storage
which the object occupies or by explicitly calling the destructor for
an object of a class type with a non-trivial destructor. For an object
of a class type with a non-trivial destructor, the program is not
required to call the destructor explicitly before the storage which
the object occupies is reused or released; however, if there is no
explicit call to the destructor or if a delete-expression (5.3.5) is
not used to release the storage, the destructor shall not be
implicitly called and any program that depends on the side effects
produced by the destructor has undefined behavior.
Note that this implies that we should not use something with a non-trivial destructor for the buffer: The elements' destructors are called at the end of the scope it's defined in. For e.g. std::string as the element type that would imply a destructor call on an non-existing array subobject, which clearly triggers undefined behavior.
If a program ends the lifetime of an object of type T with […]
automatic (3.7.3) storage duration and if T has a non-trivial
destructor, the program must ensure that an object of the original
type occupies that same storage location when the implicit destructor
call takes place; otherwise the behavior of the program is undefined.
To avoid that you would have to construct std:strings into that buffer after you're done with it, which really seems nonsensical.
Is any POD safe as buffer_type?
We do not necessarily need PODs - they have a lot of requirements that are not a necessity here.
The only things that bother us are the constructor and the destructor.
It matters whether the types destructor is trivial (or for an array, the arrays element types' destructor). Also it's feasible that the constructor is trivial, too.
POD types feel safer since they suffice both requirements and convey the idea of "bare storage" very well.
Are there restrictions on what my_type could be or not be here?
Yes. It shall be an object type. It can be any object type, but it cannot be a reference.
Any POD type would be safe. Also, while a bit dangerous, I believe most non-POD types whose destructor is empty would work here as well. If the destructor is not empty, it will be called on buffer and try to access its data which is no longer vaild (due to the placement new).