If we are implementing, for example, smart pointers, and we want to do a = std::move(b) --- we need to delete memory to which a is pointing, but can we call destructor inside move assignment operator, instead of copy-pasting destructor's function body?
Is the behavior on calling destructor inside move-assignment defined?
If it's not, are there any better ways dealing with it rather than copy-pasting destructor's body?
You are allowed to call the destructor on this inside a member function and it has well-defined behavior.
However, that behavior involves ending the lifetime of the object *this. After the destructor call you are not allowed to access/use any members of the object anymore. This also has bad consequences in many situations, e.g. if the object has automatic storage duration the destructor will be called a second time on it during scope exit, which would cause undefined behavior.
So that isn't useful for what you want to do.
Although I strongly advice against it, in theory you could then follow the destructor call by construction of a new object at the same storage location via placement-new. However there are some preconditions outside the control of the class on when this is allowed without causing undefined behavior down the line. Under some conditions such a placement-new itself might be undefined behavior, under some conditions the names/references/pointers referring to the old object will not refer to the new one causing undefined behavior if used after the placement-new and under some conditions the lifetime of any parent object that *this is a subobject/member of will also be ended by such an operation, causing undefined behavior if used afterwards.
You can see a demonstration on how this would be implemented, against my advice and under certain (unstated) assumptions, in the standard (draft): https://timsong-cpp.github.io/cppwp/n4868/basic.life#example-2
The linked example and the paragraph preceding it don't spell out all the conditions and possible problems with the approach that I hinted at above. Only the very specific usage of the class/function shown in the example is definitively allowed.
If you just want to reuse code, move the body of the destructor into a new member function and call that one from both locations requiring the same behavior.
Explictly calling destuctor is technically available, you can use this->~Object() in non static method of the class Object.
However this is a bad practice. Consider use these instead.
class Test {
public:
Test() {}
~Test() {
Deallocate();
}
Test(const Test& other) = delete;
Test(Test&& other) {
Deallocate();
// Do sth
}
private:
void Deallocate() {
// Do sth
}
};
Related
Consider:
std::vector<std::function<void()>> vec;
something_unmovable m;
vec.push_back([&vec, m]() {
vec.resize(100);
// things with 'm'
});
vec[0]();
vec.resize(100) will probably cause a re-allocation of the vector, which means that the std::functions will be copied to a new location, and the old ones destroyed. Yet this happens while the old one is still running. This particular code runs because the lambda doesn't do anything, but I imagine this can easily result in undefined behavior.
So, what happens exactly? Is m still accessible from the vector? Or is it that the this pointer of the lambda is now invalid (points to freed memory), so nothing the lambda captures can be accessible, yet if it runs code that doesn't use anything it captures, it's not undefined behavior?
Also, is the case where the lambda is movable any different?
As already covered by other answers, lambdas are essentially syntactic sugar for easily creating types that provide a custom operator() implementation. This is why you can even write lambda invocations using an explicit reference to operator(), like so: int main() { return [](){ return 0; }.operator()(); }. The same rules for all non-static member functions also apply to lambda bodies.
And those rules allow the object being destroyed while the member function is being executed, so long as the member function does not use this afterwards. Your example is an unusual one, the more common example is for a non-static member function executing delete this;. This made it into the C++ FAQ, explaining that it's allowed.
The standard allows this by not really addressing it, as far as I am aware. It describes the semantics of member functions in a way that doesn't rely on the object not being destroyed, so implementations must make sure to let member functions continue executing even if the objects get destroyed.
So to answer your questions:
Or is it that the this pointer of the lambda is now invalid (points to freed memory), so nothing the lambda captures can be accessible, yet if it runs code that doesn't use anything it captures, it's not undefined behavior?
Yes, pretty much.
Also, is the case where the lambda is movable any different?
No, it's not.
The only time where the lambda being movable could possibly matter is after the lambda has been moved. In your example, the operator() continues executing on the original moved-from and then destroyed functor.
You can treat lambda captures like ordinary struct instances.
In your case:
struct lambda_UUID_HERE_stuff
{
std::vector<std::function<void()>> &vec;
something_unmovable m;
void operator()()
{
this->vec.resize(100);
}
};
...and I believe all the same rules apply (as far as VS2013 is concerned).
So, this appears to be another case of undefined behavior. That is, if &vec happens to point to the vector containing the capture instance, and the operations within operator() cause that vector to resize.
Ultimately, there are a lot of details in this question that aren't relevant. We can reduce it to asking about the validity of:
struct A {
something_unmovable m;
void operator()() {
delete this;
// do something with m
}
};
And ask about that behavior. After all, the impact of the resize() is to call the destructor of the object mid-function-call. Whether it's moved-from or copied-from by std::vector doesn't matter - either way it will subsequently be destroyed.
The standard tells us in [class.cdtor] that:
For an object with a non-trivial
destructor, referring to any non-static member or base class of the object after the destructor finishes
execution results in undefined behavior.
So if the destructor of something_unmovable is non-trivial (which would make the destructor of A -- or your lambda -- non-trivial), any reference to m after the destructor is called is undefined behavior. If something_unmovable does have a trivial destructor, then your code is perfectly acceptable. If you don't do anything after the delete this (the resize() in your question), then it's perfectly valid behavior.
Is m still accessible from the vector?
Yes, the functor in vec[0] will still have m in it. It may be the original lambda - or it may be a copy of the original lambda. But there will be an m one way or the other.
Function objects are normally copyable, so your lambda would continue to run without ill effect. If it captures by reference AFAIR the internal implementation will use std::reference_wrapper so that the lambda remains copyable.
Consider:
std::vector<std::function<void()>> vec;
something_unmovable m;
vec.push_back([&vec, m]() {
vec.resize(100);
// things with 'm'
});
vec[0]();
vec.resize(100) will probably cause a re-allocation of the vector, which means that the std::functions will be copied to a new location, and the old ones destroyed. Yet this happens while the old one is still running. This particular code runs because the lambda doesn't do anything, but I imagine this can easily result in undefined behavior.
So, what happens exactly? Is m still accessible from the vector? Or is it that the this pointer of the lambda is now invalid (points to freed memory), so nothing the lambda captures can be accessible, yet if it runs code that doesn't use anything it captures, it's not undefined behavior?
Also, is the case where the lambda is movable any different?
As already covered by other answers, lambdas are essentially syntactic sugar for easily creating types that provide a custom operator() implementation. This is why you can even write lambda invocations using an explicit reference to operator(), like so: int main() { return [](){ return 0; }.operator()(); }. The same rules for all non-static member functions also apply to lambda bodies.
And those rules allow the object being destroyed while the member function is being executed, so long as the member function does not use this afterwards. Your example is an unusual one, the more common example is for a non-static member function executing delete this;. This made it into the C++ FAQ, explaining that it's allowed.
The standard allows this by not really addressing it, as far as I am aware. It describes the semantics of member functions in a way that doesn't rely on the object not being destroyed, so implementations must make sure to let member functions continue executing even if the objects get destroyed.
So to answer your questions:
Or is it that the this pointer of the lambda is now invalid (points to freed memory), so nothing the lambda captures can be accessible, yet if it runs code that doesn't use anything it captures, it's not undefined behavior?
Yes, pretty much.
Also, is the case where the lambda is movable any different?
No, it's not.
The only time where the lambda being movable could possibly matter is after the lambda has been moved. In your example, the operator() continues executing on the original moved-from and then destroyed functor.
You can treat lambda captures like ordinary struct instances.
In your case:
struct lambda_UUID_HERE_stuff
{
std::vector<std::function<void()>> &vec;
something_unmovable m;
void operator()()
{
this->vec.resize(100);
}
};
...and I believe all the same rules apply (as far as VS2013 is concerned).
So, this appears to be another case of undefined behavior. That is, if &vec happens to point to the vector containing the capture instance, and the operations within operator() cause that vector to resize.
Ultimately, there are a lot of details in this question that aren't relevant. We can reduce it to asking about the validity of:
struct A {
something_unmovable m;
void operator()() {
delete this;
// do something with m
}
};
And ask about that behavior. After all, the impact of the resize() is to call the destructor of the object mid-function-call. Whether it's moved-from or copied-from by std::vector doesn't matter - either way it will subsequently be destroyed.
The standard tells us in [class.cdtor] that:
For an object with a non-trivial
destructor, referring to any non-static member or base class of the object after the destructor finishes
execution results in undefined behavior.
So if the destructor of something_unmovable is non-trivial (which would make the destructor of A -- or your lambda -- non-trivial), any reference to m after the destructor is called is undefined behavior. If something_unmovable does have a trivial destructor, then your code is perfectly acceptable. If you don't do anything after the delete this (the resize() in your question), then it's perfectly valid behavior.
Is m still accessible from the vector?
Yes, the functor in vec[0] will still have m in it. It may be the original lambda - or it may be a copy of the original lambda. But there will be an m one way or the other.
Function objects are normally copyable, so your lambda would continue to run without ill effect. If it captures by reference AFAIR the internal implementation will use std::reference_wrapper so that the lambda remains copyable.
This answer quotes C++11 Standard 3.8:
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.
The part about the destructor not being called is clear. Now suppose the skipped destructor had a side effect that should have affected the program behavior.
Why is the program behavior undefined now? Why wouldn't the side effects be skipped (since the destructor is not called) and the program run normally just without side effects applied?
The important part is the first part of that paragraph (emphasis mine):
A program may end the lifetime of any object by reusing the storage which the object occupies ...
If you simply reuse the storage for an object whose destructor has not been called, then you get undefined behaviour. For example, the object could have started a thread, or registered a callback, or some other action where an external component might expect the object to still exist.
In this case, we do have a precise answer. The specific line was introduced to resolve CWG 1116, "Aliasing of union members".
Your question does not make sense.
Why wouldn't the side effects be skipped (since the destructor is not called) and the program run normally just without side effects applied?
They are skipped, because they would have been triggered by the destructor and it has not been called.
My reading of:
and any program that depends on the side effects produced by the destructor has undefined behavior.
is simple, I view it in light of RAII. Example:
#include "Object.hpp"
struct Manager: private boost::noncopyable {
union Raw {
char _[sizeof(Object)];
Object o;
};
static Raw raw;
Manager() { new (raw.o) Object(); }
~Manager() { raw.o.~Object(); }
};
Now, if I allocate a Manager, forgets to destroy it, and allocates a new one, I am in a pinch for I am overwriting the storage of the first created Object with a second one even though it is still "alive". This is undefined behavior.
I believe this is put into the standard to allow for garbage collection. And to point out that C++ behaves differently that some other languages, by not calling destructors while collecting.
It specifically says that storage can be reused without calling the destructors of the objects in that memory area. If the program depends on the destructors being run, it will not work as expected.
If it doesn't depend on the destructors, all is fine.
Herb Sutter mentions in one of his http://www.gotw.ca articles that an object is constructed(has valid existence) only if the constructor executes completes.ie to put it in a crude way control passes beyond its final brace.
Now consider the following code
class A
{
public:
A()
{
f();
}
void f()
{
cout << "hello, world";
}
};
int main()
{
A a;
}
Now from what Herb says, can't we say that since A is not completely constructed inside its constructor Calling f() inside the constructor is invalid as the "this" ptr is not ready yet.
Still there is indeed a valid "this" inside the constructor and f() does get called.
I don't think Herb is saying something incorrect... but guess i am interpreting it incorrectly....can some explain to me what exactly that is?
Here is the link to the article : http://www.gotw.ca/gotw/066.htm
It talks about exceptions from constructors. Specifically here is the extract from it on which my question is based:
-When does an object's lifetime begin?
When its constructor completes successfully and returns normally. That is, control reaches the end of the constructor body or an earlier return statement.
-When does an object's lifetime end?
When its destructor begins. That is, control reaches the beginning of the destructor body.
Important point here is that the state of the object before its lifetime begins is exactly the same as after its lifetime ends -- there is no object, period. This observation brings us to the key question:
We might summarize the C++ constructor model as follows:
Either:
(a) The constructor returns normally by reaching its end or a return statement, and the object exists.
Or:
(b) The constructor exits by emitting an exception, and the object not only does not now exist, but never existed.
Now from what Herb says, can't we say
that since A is not completely
constructed inside its constructor
Calling f() inside the constructor is
invalid as the "this" ptr is not ready
yet.
That is only when f() is a virtual method of class A or its inheritance hierarchy and you expect the runtime resolution for f() according to the right object. In simple words, virtual mechanism doesn't kick in if the method is invoked inside constructor.
If f() is not a virtual function, there is no harm in calling it from constructor(s) provided you know what exactly f() does. Programmers usually call class methods like initialize() from constructor(s).
Can you give me the link to the Herb Sutter's article?
By the time program flow enters your constructor, the object's memory has been allocated and the this pointer is indeed valid.
What Herb means, is that the object's state may not have entirely initialized. In particular, if you are constructing a class derived from A, then that class' constructor will not have been called while you are still inside A's constructor.
This is important if you have virtual member functions, since any virtual function in the derived class will not be run if called from within A's constructor.
Note: it would have been easier with the exact article, so that we could have some context
Lifetime considerations are actually pretty complicated.
Considering the constructor of an object, there are two different point of views:
external: ie the user of an object
internal: ie, you when writing constructors and destructors (notably)
From the external point of view, the lifetime of an object:
begins once the constructor successfully completed
ends when the destructor begins to run
It means that if you attempt to access an object mid-construction or mid-destruction Bad Things Happen (tm). This is mostly relevant to multi-threaded programs, but may happen if you pass pointers to your object to base classes... which leads to...
...the internal point of view. It's more complicated. One thing you are sure of is that the required memory has been allocated, however parts of the objects may not be fully initialized yet (after all, you are constructing it).
in the body of the constructor, you can use the attributes and bases of the class (they are initialized), and call functions normally (virtual calls should be avoided).
if it's a base class, the derived object is not initialized yet (thus the restriction on virtual calls)
The implication from the lifetime not having started yet is mainly that, should the constructor throw an exception, the destructor will not be run.
Beware of member variables that are not yet initialized. Beware of virtual functions: the function that you call might not be the one that you expect if the function is virtual and a derived object is created. Other than that, I do not see any problem calling methods from the constructor. Especially the memory for the object has already been allocated.
In my opinion, the following code (from some C++ question) should lead to UB, but the it seems it is not. Here is the code:
#include <iostream>
using namespace std;
class some{ public: ~some() { cout<<"some's destructor"<<endl; } };
int main() { some s; s.~some(); }
and the answer is:
some's destructor
some's destructor
I learned form c++ faq lite that we should not explicitly call destructor. I think after the explicitly call to the destructor, the object s should be deleted. The program automatically calls the destructor again when it's finished, it should be UB. However, I tried it on g++, and get the same result as the above answer.
Is it because the class is too simple (no new/delete involved)? Or it's not UB at all in this case?
The behavior is undefined because the destructor is invoked twice for the same object:
Once when you invoke it explicitly
Once when the scope ends and the automatic variable is destroyed
Invoking the destructor on an object whose lifetime has ended results in undefined behavior per C++03 §12.4/6:
the behavior is undefined if the destructor is invoked for an object whose lifetime has ended
An object's lifetime ends when its destructor is called per §3.8/1:
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.
Note that this means if your class has a trivial destructor, the behavior is well-defined because the lifetime of an object of such a type does not end until its storage is released, which for automatic variables does not happen until the end of the function. Of course, I don't know why you would explicitly invoke the destructor if it is trivial.
What is a trivial destructor? §12.4/3 says:
A destructor is trivial if it is an implicitly-declared destructor and if:
— all of the direct base classes of its class have trivial destructors and
— for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.
As others have mentioned, one possible result of undefined behavior is your program appearing to continue running correctly; another possible result is your program crashing. Anything can happen and there are no guarantees whatsoever.
It's undefined behavior -- but as with any UB, one possibility is that it (more or less) appears to work, at least for some definition of work.
Essentially the only time you need (or want) to explicitly invoke a destructor is in conjunction with placement new (i.e., you use placement new to create an object at a specified location, and an explicit dtor invocation to destroy that object).
From http://www.devx.com/tips/Tip/12684
Undefined behavior indicates that an implementation may behave unpredictably when a program reaches a certain state, which almost without exception is a result of a bug. Undefined behavior can be manifested as a run time crash, unstable and unreliable program state, or--in rare cases--it may even pass unnoticed.
In your case it doesn't crash because the destructor doesn't manipulate any field; actually, your class doesn't have any data members at all. If it did and in destructor's body you manipulated it in any way, you would likely get a run-time exception while calling destructor for the second time.
The problem here is that deletion / deallocation and destructors are separate and independent constructs. Much like new / allocation and constructors. It is possible to do only one of the above without the other.
In the general case this scenario does lack usefulness and just lead to confusion with stack allocated values. Off the top of my head I can't think of a good scenario where you would want to do this (although I'm sure there is potentially one). However it is possible to think of contrived scenarios where this would be legal.
class StackPointer<T> {
T* m_pData;
public:
StackPointer(T* pData) :m_pData(pData) {}
~StackPointer() {
delete m_pData;
m_pData = NULL;
}
StackPointer& operator=(T* pOther) {
this->~StackPointer();
m_pData = pOther;
return this;
}
};
Note: Please don't ever code a class this way. Have an explicit Release method instead.
It most likely works fine because the destructor does not reference any class member variables. If you tried to delete a variable within the destructor you would probably run into trouble when it is automatically called the second time.
Then again, with undefined behavior, who knows? :)
What the main function does is reserving space on the stack, calling some's constructor, and at the end calling some's destructor. This always happens with a local variable, whatever code you put inside the function.
Your compiler won't detect that you manually called the destructor.
Anyway you should never manually call an object's destructor, except for objects created with placement-new.
I believe that if you want your code to be OK you simply need to call placement new and fill it back in before exiting. The call to the destructor isn't the issue, it's the second call to the destructor made when you leave scope.
Can you define the undefined behaviour you expect? Undefined doesn't mean random (or catastrophic): the behaviour of a given program may be repeatable between invocations, it just means you can't RELY on any particular behaviour because it is undefined and there is no guarantee of what will happen.
It is undefined behaviour. The undefined behaviour is the double destructor call and not with the destructor call itself. If you modify your example to:
#include <iostream>
using namespace std;
class some{ public: ~some() { [INSERT ANY CODE HERE] } };
int main() { some s; s.~some(); }
where [INSERT ANY CODE HERE] can be replaced with any arbitrary code. The results have unpredictable side effects, which is why it is considered undefined.