John Z. Li
John Z. Li

Reputation: 1995

Any reason behind the syntax difference between shared pointers and unique pointers with custom deleters

While both shared pointers and unique pointers in C++11 allow user defined deleters, they have notable syntax differences, as shown in the below mini example:

#include "pch.h"
#include <memory>
class factory
{
public:
    static factory * create() {
        return new factory();
    }
    static void destroy(factory*    fp) noexcept{
        delete fp;
    }
    factory(const factory &) = delete;
    factory& operator= (const factory &) = delete;
private:
    char * p_;
    factory() {
    p_ = new char[100];
    }
    ~factory() {
        delete[] p_;
    }
};

int main()
{
    typedef void(*fp)(factory*);

    auto del = [](factory * p) noexcept {
        factory::destroy(p);
    };

    std::shared_ptr<factory> fsptr1(factory::create(), del);
    std::shared_ptr<factory> fsptr2(factory::create(), del);
    //notice the syntax is different
    std::unique_ptr<factory, fp> ufsptr1(factory::create(), del);
    std::unique_ptr<factory, decltype(del)> ufsptr2(factory::create(), del);

    return 0;
}

The reason behind this is that the template class for shared pointers is defined as

template< class T > class shared_ptr;

and the template class for unique pointers is defined as

template<class T, class Deleter = std::default_delete<T>> class unique_ptr;

My question is: Is there any reason behind this design decision that the syntax of the two differentiates itself from each other? My naive thinking is that if the template class for shared pointers is made as

template< class T, class Deleter = std::default_delete<T>> class shared_ptr;

it will makes more sense. For one thing, it is consistent with the case of unique pointers, and for another, it won't instantiate when the default deleter is not well formed and the user fails to provide a custom one.

Upvotes: 2

Views: 90

Answers (1)

Matthieu Brucher
Matthieu Brucher

Reputation: 22023

The default std::unique_ptr is only storing one element, the pointer to the data it protects. This is because by default, you want to use the least amount of memory possible. But when you specify a deleter, you need also to store it. So you need to differentiate between the two versions.

See here: https://github.com/llvm-mirror/libcxx/blob/master/include/memory#L2397

The storage is a specific type based on the template types.

But for std::shared_ptr, you don't have this constraint, you already have a counter, you need to allocate a block to store it. So you can make the deletion choice inside the allocation logic instead of outside, at the API level.

See here: https://github.com/llvm-mirror/libcxx/blob/master/include/memory#L3592

The compressed_pair is not in the smart pointer itself, but in the allocated block.

Upvotes: 5

Related Questions