Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why unique_ptr with custom deleter won't work for nullptr, while shared_ptr does?

Simple code to use either unique_ptr or shared_ptr as a scope guard. All information about what to clear is captured in the deleter, so I though it is safe to use nullptr for constructor.

Apparently, with Visual C++ 2017 (14.1), it is not working as expected for unique_ptr, but works for shared_ptr. Is it a Microsoft quirk, or does the standard prevent calling the deleter of a unique_ptr when holding nullptr?

In the code below, I'm forced to construct a unique_ptr with (void*)1. If I construct it with nullptr, cleaner won't be called. For shared_ptr, there is no difference, cleaner is always called.

#include <memory>
#include <iostream>

int main()
{
    int ttt = 77;

    auto cleaner = [&ttt](void*) {
        std::cout << "cleaner: " << ttt << "\n"; // do something with capture here instead of print
    };

    std::unique_ptr<void, decltype(cleaner)> p((void*)1, cleaner);

    std::shared_ptr<void> q(nullptr, [&ttt](void*) {
        std::cout << "shared: " << ttt << "\n"; // do something with capture here instead of print
    });

    std::cout << "done\n";
    return 0;
}
like image 211
Severin Pappadeux Avatar asked May 06 '18 16:05

Severin Pappadeux


1 Answers

unique_ptr's destructor is required to do so:

23.11.1.2.2 unique_ptr destructor [unique.ptr.single.dtor]

2 Effects: If get() == nullptr there are no effects. Otherwise get_deleter()(get()).

actually shared_ptr's destructor is required to do the same:

23.11.2.2.2 shared_ptr destructor [util.smartptr.shared.dest]

— (1.1) If *this is empty or shares ownership with another shared_ptr instance ( use_count() > 1 ), there are no side effects.

— (1.2) Otherwise, if *this owns an object p and a deleter d, d(p) is called.

So relying on smart pointers to perform arbitrary actions at scope exit while passing null pointers is not reliable.

like image 103
user7860670 Avatar answered Oct 19 '22 13:10

user7860670