neuront
neuront

Reputation: 9612

Can I rebind the deleter type for unique_ptr?

Got some questions when using the unique_ptr and tried to read the source but more questions emerge.

The template parameter Deleter in unique_ptr<class Tp, class Deleter = default_delete<Tp>> is a class other than a template so I wonder if it uses some tricks like the allocator::rebind (discussion found here). But no, it must have an exact operator()(pointer) overload.

So I turned to the STL source (in Ubuntu 14.04 gcc 4.8) and find that a nested _Pointer class is defined for some type traits, but absolutely nothing to do with rebinding the Deleter type to something compatible with the pointer type.

// /usr/include/c++/4.8/bits/unique_ptr.h:112
class _Pointer
{
    template<typename _Up>
    static typename _Up::pointer __test(typename _Up::pointer*);

    template<typename _Up>
    static _Tp* __test(...);

    // _Dp is the Deleter type
    typedef typename remove_reference<_Dp>::type _Del;
public:
    typedef decltype(__test<_Del>(0)) type;
};

// /usr/include/c++/4.8/bits/unique_ptr.h:130
// adopt the pointer type provided by the _Pointer
typedef typename _Pointer::type   pointer;

I've no idea what that class is supposed to do but it seems the Tp parameter can be overrided. To test it I've fabricated some weird codes like this

struct MyDeleter {
    // in the deleter type, define the pointer as pointer to int
    typedef int* pointer;

    void operator()(pointer p)
    {
        delete p;
    }
};

int main()
{
    // this won't compile!
    std::unique_ptr<double, MyDeleter> p(new double(0));

    // this type is equivalent to std::unique_ptr<int, MyDeleter>
    std::unique_ptr<double, MyDeleter> p(new int(0));
    return 0;
}

I'm completely confused. Would anybody explain that

Upvotes: 3

Views: 177

Answers (1)

Bo Persson
Bo Persson

Reputation: 92271

The rules say that if MyDeleter has a nested type pointer, then that type is used. Otherwise std::unique_ptr<double, MyDeleter> will use double* formed from the first parameter.

And the pointer you store must match the resulting pointer type of the unique_ptr, otherwise the final delete would be UB.

So, in the first case of std::unique_ptr<double, MyDeleter> p(new double(0)); you try to store a double* when the deleter expects an int*. That will never work, so it's good that the compiler catches it.

Upvotes: 1

Related Questions