Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Destructor elision

33 views
Skip to first unread message

Thiago Adams

unread,
Jul 19, 2018, 10:06:17 AM7/19/18
to
Something I would like to see in C++ is the
idea of "destructor elision".

Similarly of c++ copy elision, where the compiler
is allowed to change the observed behavior, I would
like to give the compiler the chance to remove the
destructor call and change the observable behavior.

Basically this feature gives us optimization after
to move objects.


std::unique_ptr<int> p1 = std::make_unique<int>();
std::unique_ptr<int> p2;
p2 = std::move(p1);

in this case, the compiler would be allowed to remove
the destructor call of the p2.


Thiago Adams

unread,
Jul 19, 2018, 10:17:18 AM7/19/18
to
Correction: (p1 - not p2)

..In this case, the compiler would be allowed to remove
the destructor call of the p1.



Thiago Adams

unread,
Jul 19, 2018, 10:39:10 AM7/19/18
to
This feature also would allow us to call destructors
before the end of scope.

Sample of scope created to call the destructor:

//...
{
Lock lock;
//..something
}
//...

We could write:

//...
Lock lock;
//..something
std::destroy(lock); //move to nothing
//...


and them this feature give us the same control
we have in manual C code, plus the guarantee
of RAII.


Bo Persson

unread,
Jul 19, 2018, 2:19:18 PM7/19/18
to
> the destructor call of the p1.
>
>

You know, perhaps it already does that?

Here is the result on MSVC 2017:


#include <memory>
int main()
{
00007FF7893D1000 sub rsp,28h
std::unique_ptr<int> p1 = std::make_unique<int>();
00007FF7893D1004 mov ecx,4
00007FF7893D1009 call operator new (07FF7893D1024h)
00007FF7893D100E xor ecx,ecx
00007FF7893D1010 mov dword ptr [rax],ecx
std::unique_ptr<int> p2;
p2 = std::move(p1);

}
00007FF7893D1012 lea edx,[rcx+4]
00007FF7893D1015 mov rcx,rax
00007FF7893D1018 call operator delete (07FF7893D1060h)
00007FF7893D101D xor eax,eax
00007FF7893D101F add rsp,28h
00007FF7893D1023 ret


What destructor call do you want to remove?


Bo Persson

Thiago Adams

unread,
Jul 19, 2018, 2:53:31 PM7/19/18
to
I don't want to call the destructor of the moved
object. (p1)

MSVC also removed the p1 going to zero after move.

I think this optimization is for inline code like this:


{
int * p1 = new int();
int *p2;
p2 = p1;
p1 = 0; //<--- goes to 0
delete p2;
if (p1 != 0) //compiler knows that p1 is always zero
{
//removed
delete p1;
}
}

I will try something not inline just to see what happens.



Thiago Adams

unread,
Jul 19, 2018, 3:12:56 PM7/19/18
to
On Thursday, July 19, 2018 at 3:53:31 PM UTC-3, Thiago Adams wrote:
...
> I will try something not inline just to see what happens.

I created an object similar of unique_ptr but not template
and not inline (the source was in a separated cpp file)
and the compiler did the optimization as well.



Then I added an printf to the destructor and the compiler
did the optimization as well, leaving just the printf
and removing the if and delete.

IntUniquePtr::~IntUniquePtr()
{
printf("a");
if (p)
delete p;
}


So the optimization was like inline code. Maybe VC++ is
doing inline even when we didn't put the implementation
together with the class declaration - this is my guess.




Adam Wysocki

unread,
Jul 19, 2018, 3:17:50 PM7/19/18
to
Thiago Adams <thiago...@gmail.com> wrote:

> std::unique_ptr<int> p1 = std::make_unique<int>();
> std::unique_ptr<int> p2;
> p2 = std::move(p1);
>
> in this case, the compiler would be allowed to remove
> the destructor call of the p2.

It does so.

Consider this code:

#v+
#include <memory>
#include <iostream>

class C
{
public:
C() { std::cout << "ctor" << std::endl; }
~C() { std::cout << "dtor" << std::endl; }
};

int main()
{
std::unique_ptr<C> c1(std::make_unique<C>());
std::cout << "between" << std::endl;
std::unique_ptr<C> c2(std::move(c1));
}
#v-

I compiled it with g++ 6.3.0 (with -std=c++14) and it produced:

#v+
ctor
between
dtor
#v-

--
[ Adam Wysocki :: Warsaw, Poland ]
[ Email: a@b a=grp b=chmurka.net ]
[ Web: http://www.chmurka.net/ ]

Manfred

unread,
Jul 19, 2018, 3:23:32 PM7/19/18
to
This does not take into account the constructors (specifically the move
constructor) and destructor of the unique_ptr, only those of C.

Thiago Adams

unread,
Jul 19, 2018, 3:26:36 PM7/19/18
to
On Thursday, July 19, 2018 at 4:17:50 PM UTC-3, Adam Wysocki wrote:
In this case, the dtor of C will not be printed twice
even in debug because the pointer is null.
the print must be at the unique_ptr object.

Thiago Adams

unread,
Jul 19, 2018, 4:33:02 PM7/19/18
to
On Thursday, July 19, 2018 at 4:12:56 PM UTC-3, Thiago Adams wrote:
...

> So the optimization was like inline code. Maybe VC++ is
> doing inline even when we didn't put the implementation
> together with the class declaration - this is my guess.

Just for curiosity:
This code in C also removes the second call to free.

int main()
{
struct X * p1 = malloc(sizeof * p1);
struct X * p2;
p2 = p1;
p2 = 0;

X_Delete(p1);
X_Delete(p2);

return 0;
}

void X_Delete(struct X* pX)
{
if (pX) {
free(pX);
}
}


But in case I remove the "if"

if (pX) {
free(pX);
}

and leave just

free(pX);

then the free is called twice.

The optimizer follow the state of p2 and removes
everything inside the if.






Thiago Adams

unread,
Jul 19, 2018, 4:43:09 PM7/19/18
to
One difference from optimizations and "Destructor elision" is that
even when the destructor has some code, let's say a log, or printf,
the compiler is not required to call this destructor.
(similar of copy elision)

0 new messages