segfault
segfault

Reputation: 519

Can a smart pointer be optimized away?

Mind the code

...
{
    int* p = new int(0);
    std::unique_ptr<int> q(p);
    ...
    // make use of 'p'
}
...

In the code above, the unique pointer q is used solely to free p, when time comes. Q is not used by itself.
Since q is never used below the line where it is declared, it can seemingly be released immediately after being declared, thus making use of p "use after free".
The question is q guaranteed to live on until going out of the current scope, or the compiler's optimizer is free to free it before ?

Upvotes: 8

Views: 587

Answers (2)

segfault
segfault

Reputation: 519

I've realized the answer to my own question:
In the code

{
    int* p = new int(0);
    std::unique_ptr<int> q(p);
    ...
    // HERE
}

the destructor of q is guaranteed to be called upon scope exit (HERE).
While the compiler is allowed to allocate and free registers for variables as it pleases, the destructor is guaranteed to be called in a particular place - scope exit.
How do I know ?
Because this is what enables C++ scope guard. Oftentimes the scope guard is used to release a mutex on scope exit - this is something that needs to be guaranteed.

Upvotes: 0

Jarod42
Jarod42

Reputation: 217810

With the as-if rule, compiler is allowed to do any optimization as long as observable behavior is identical.

Freeing immediately q/p would not be allowed, as then you will use dangling pointer.

Though it can call destructor before end of scope:

{
    int* p = new int(0);
    std::unique_ptr<int> q(p);
    ...
    // make use of 'p'
    ...
    // No longer use of p (and q)
    ...
    // Ok, can delete p/q now (as long there are no observable behaviors changes)
    ...
}

As operator new/delete might be changed globally, compiler would generally not have enough information (linker has though), so consider they have (potentially) observable behaviors (as any external functions).

c++14 allows some elisions/optimisation of new expression, so

{
    delete new int(42);
    int* p1 = new int(0);
    int* p2 = new int(0);
    std::unique_ptr<int> q2(p2);
    std::unique_ptr<int> q1(p1);
    ...
    // make use of 'p1'/p2
    ...
}

Can be "replaced" by

{
    // delete new int(42); // optimized out
    std::unique_ptr<int[]> qs{new int [] {0, 0}}; // only one allocation instead of 2
    int* p1 = q->get();
    int* p2 = q->get() + 1;
    ...
    // make use of 'p1'/p2
    ...
}

Upvotes: 5

Related Questions