web-dev-qa-db-ja.com

shared_ptrが1つしか取得しないのに、unique_ptrが2つのテンプレートパラメータを取得するのはなぜですか?

unique_ptrshared_ptr はどちらも、所有するオブジェクトを呼び出すカスタムデストラクタを受け入れます。ただし、unique_ptrの場合、デストラクタはクラスのテンプレートパラメータとして渡されますが、shared_ptrのタイプはカスタムデストラクタは、コンストラクタのテンプレートパラメータとして指定されます。

template <class T, class D = default_delete<T>> 
class unique_ptr
{
    unique_ptr(T*, D&); //simplified
    ...
};

そして

template<class T>
class shared_ptr
{
    template<typename D>
    shared_ptr(T*, D); //simplified
    ...
};

なぜそんな違いがあるのか​​わかりません。何が必要ですか?

66
qdii

テンプレート引数として削除機能を指定した場合(unique_ptrのように)、削除機能は型の一部であり、この型のオブジェクトに追加のものを格納する必要はありません。 (shared_ptrのように)deleterがコンストラクターの引数として渡される場合は、それをオブジェクトに格納する必要があります。同じタイプのオブジェクトに異なる削除機能を使用できるため、これは柔軟性を高めるためのコストです。

これが理由だと思います。unique_ptrは、オーバーヘッドがゼロの非常に軽量なオブジェクトであると想定されています。 unique_ptrごとに削除機能を保存すると、サイズが2倍になる可能性があります。そのため、人々は代わりに古き良き生のポインタを使用するでしょう、それは間違っているでしょう。

一方、shared_ptrは、参照カウントを保存する必要があるため、それほど軽量ではありません。したがって、カスタム削除機能を保存することも、適切なトレードオフのように見えます。

57
Wojtek Surowka

異なるタイプの共有ポインタは、同じオブジェクトの所有権を共有できます。 overload(8) of std::shared_ptr::shared_ptrを参照してください。一意のポインタは共有しないため、このようなメカニズムは必要ありません。

template< class Y > 
shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;

削除機能をタイプ消去しないと、そのようなshared_ptr<T, Y_Deleter>shared_ptr<T>として使用できなくなり、基本的に役に立たなくなります。

なぜあなたはそのような過負荷が欲しいのですか?

検討する

struct Member {};
struct Container { Member member };

Containerを使用しているときに、Memberを存続させたい場合は、次のことができます。

std::shared_ptr<Container> pContainer = /* something */
std::shared_ptr<Member> pMember(pContainer, &pContainer->member);

pMemberを保持するだけで済みます(おそらくそれをstd::vector<std::shared_ptr<Member>>に入れます)

または、代わりに、過負荷を使用します(9)

template< class Y > 
shared_ptr( const shared_ptr<Y>& r ) noexcept; 
  // Only exists if Y* is implicitly convertible to T*

あなたは多形の共有を持つことができます

struct Base {};
struct Derived : Base {};

void operate_on_base(std::shared_ptr<Base>);

std::shared_ptr<Derived> pDerived = /* something*/
operate_on_base(pDerived);
1
Caleth