サーチ…


構文

  • std::shared_ptr<ClassType> variableName = std::make_shared<ClassType>(arg1, arg2, ...);
  • std::shared_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));
  • std::unique_ptr<ClassType> variableName = std::make_unique<ClassType>(arg1, arg2, ...); // C ++ 14
  • std::unique_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));

備考

C ++はメモリ管理言語ではありません。動的に割り当てられたメモリ( newオブジェクトで作成されたオブジェクト)は、明示的に割り当て解除されていない( delete )場合、 "リーク"されます。そのオブジェクトへの最後のポインタを破棄する前に、動的に割り当てられたメモリが解放されることを保証するのはプログラマの責任です。

スマートポインタを使用して、動的に割り当てられたメモリの範囲を自動的に管理することができます(つまり、最後のポインタ参照が範囲外になったときに削除されます)。

スマートポインタは、ほとんどの場合、「生の」ポインタよりも優先されます。動的に割り当てられたメモリの所有権セマンティクスを、オブジェクトが共有または一意に所有されることを意図しているかどうか、その名前で通信することによって明示的にします。

スマートポインタを使用できるようにするには、 #include <memory>を使用します。

所有権の共有(std :: shared_ptr)

クラステンプレートstd::shared_ptrは、オブジェクトの所有権を他の共有ポインタと共有できる共有ポインタを定義します。これは、排他的所有権を表すstd::unique_ptrとは対照的です。

共有動作は、オブジェクトを指す共有ポインタの数がそのオブジェクトの横に格納される参照カウントと呼ばれる手法によって実装されます。最後のstd::shared_ptrインスタンスの破棄または再割り当てのいずれかによってこの数がゼロになると、オブジェクトは自動的に破棄されます。


// Creation: 'firstShared' is a shared pointer for a new instance of 'Foo'
std::shared_ptr<Foo> firstShared = std::make_shared<Foo>(/*args*/);

同じオブジェクトを共有する複数のスマートポインタを作成するには、最初の共有ポインタの別名である別のshared_ptrを作成する必要があります。それを行うには2つの方法があります:

std::shared_ptr<Foo> secondShared(firstShared);  // 1st way: Copy constructing
std::shared_ptr<Foo> secondShared;
secondShared = firstShared;                      // 2nd way: Assigning

上記のいずれかの方法では作るsecondSharedの私達のインスタンスの所有権を共有し、共有ポインタFoofirstShared

スマートポインタは生ポインタと同じように機能します。つまり、 *を使用して逆参照することができます。 regular ->演算子も同様に動作します:

secondShared->test(); // Calls Foo::test()

最後にエイリアス化されたshared_ptrがスコープから外れると、 Fooインスタンスのデストラクタが呼び出されます。

警告:共有所有権セマンティクスの余分なデータを割り当てる必要がある場合、 shared_ptrを作成するとbad_alloc例外がスローされることがあります。コンストラクタに通常のポインタが渡された場合、それは指し示されているオブジェクトを所有するものとみなされ、例外がスローされた場合にはディレクタを呼び出します。つまり、 shared_ptr<T>(new T(args))shared_ptr<T>割り当てが失敗した場合にTオブジェクトをリークしません。ただし、 make_shared<T>(args)またはallocate_shared<T>(alloc, args)を使用することをお勧めします。これにより、メモリ割り当てが最適化されます。


shared_ptrを使用して配列([])を割り当てる

C ++ 11 C ++ 17

残念ながら、 make_shared<>を使用して配列を割り当てる直接的な方法はありません。

newstd::default_deleteを使用してshared_ptr<>配列を作成することは可能です。

たとえば、10個の整数の配列を割り当てるには、次のようにコードを書くことができます。

shared_ptr<int> sh(new int[10], std::default_delete<int[]>());

割り当てられたメモリがdelete[]を使用して正しくクリーンアップされるように、 std::default_delete指定することは必須です。

コンパイル時にサイズを知っていれば、次のようにすることができます:

template<class Arr>
struct shared_array_maker {};
template<class T, std::size_t N>
struct shared_array_maker<T[N]> {
  std::shared_ptr<T> operator()const{
    auto r = std::make_shared<std::array<T,N>>();
    if (!r) return {};
    return {r.data(), r};
  }
};
template<class Arr>
auto make_shared_array()
-> decltype( shared_array_maker<Arr>{}() )
{ return shared_array_maker<Arr>{}(); }

make_shared_array<int[10]>は、デフォルトで構築された10個のintを指すshared_ptr<int>返します。

C ++ 17

C ++ 17では、 shared_ptr 配列型を特別にサポートしていました。 array-deleterを明示的に指定する必要はなく、 []配列インデックス演算子を使用して共有ポインタを参照解除することができます。

std::shared_ptr<int[]> sh(new int[10]);
sh[0] = 42;

共有ポインタは、自身が所有するオブジェクトのサブオブジェクトを指すことができます。

struct Foo { int x; };
std::shared_ptr<Foo> p1 = std::make_shared<Foo>();
std::shared_ptr<int> p2(p1, &p1->x);

p2p1両方が型Fooのオブジェクトを所有しますが、 p2はそのintメンバx指しています。つまり、 p1が有効範囲外になったり再割り当てされたりすると、元のFooオブジェクトは引き続き有効になり、 p2がぶら下がらないことが保証されます。


重要: shared_ptrは、エイリアスコンストラクタで作成されたshared_ptr自体とそれ以外のすべてのshared_ptrのみを認識します。同じFooインスタンスへの参照で作成された他のすべてのshared_ptrを含む他のポインタについてはわかりません:

Foo *foo = new Foo;
std::shared_ptr<Foo> shared1(foo);
std::shared_ptr<Foo> shared2(foo); // don't do this

shared1.reset(); // this will delete foo, since shared1
                 // was the only shared_ptr that owned it

shared2->test(); // UNDEFINED BEHAVIOR: shared2's foo has been
                 // deleted already!!

shared_ptrの所有権移転

デフォルトでは、 shared_ptrは参照カウントをインクリメントし、所有権を転送しません。ただし、 std::moveを使用して所有権を転送することができます。

shared_ptr<int> up = make_shared<int>();
// Transferring the ownership
shared_ptr<int> up2 = move(up);
// At this point, the reference count of up = 0 and the
// ownership of the pointer is solely with up2 with reference count = 1

一時所有権を持つ共有(std :: weak_ptr)

std::weak_ptrインスタンスは、 std::shared_ptrインスタンスが所有するオブジェクトを指すことができますが、一時的な所有者になることができます。これは、ウィークポインタがオブジェクトの参照カウントを変更しないため、オブジェクトの共有ポインタのすべてが再割り当てまたは破棄された場合、オブジェクトの削除を防止しないことを意味します。


次の例では、 std::weak_ptrインスタンスが使用されているため、ツリーオブジェクトの破棄は禁止されていません。

#include <memory>
#include <vector>

struct TreeNode {
    std::weak_ptr<TreeNode> parent;
    std::vector< std::shared_ptr<TreeNode> > children;
};

int main() {
    // Create a TreeNode to serve as the root/parent.
    std::shared_ptr<TreeNode> root(new TreeNode);

    // Give the parent 100 child nodes.
    for (size_t i = 0; i < 100; ++i) {
        std::shared_ptr<TreeNode> child(new TreeNode);
        root->children.push_back(child);
        child->parent = root;
    }

    // Reset the root shared pointer, destroying the root object, and
    // subsequently its child nodes.
    root.reset();
}

子ノードがルートノードの子ノードに追加されると、そのstd::weak_ptrメンバーのparentノードがルートノードに設定されます。メンバーのparentは、ルートポインタの参照カウントがインクリメントされないように、共有ポインタとは対照的にウィークポインタとして宣言されます。ルートノードがmain()最後でリセットされると、ルートは破棄されます。子ノードへの残りのstd::shared_ptr参照のみがルートのコレクションのchildrenに含まれていたため、すべての子ノードも後で破棄されます。

制御ブロックの実装の詳細により、 shared_ptr参照カウンタとweak_ptr参照カウンタの両方がゼロに達するまで、shared_ptr割り当てメモリは解放されないことがあります。

#include <memory>
int main()
{
    {
         std::weak_ptr<int> wk;
         {
             // std::make_shared is optimized by allocating only once 
             // while std::shared_ptr<int>(new int(42)) allocates twice.
             // Drawback of std::make_shared is that control block is tied to our integer
             std::shared_ptr<int> sh = std::make_shared<int>(42);
             wk = sh;
             // sh memory should be released at this point...
         }
         // ... but wk is still alive and needs access to control block
     }
     // now memory is released (sh and wk)
}

以来std::weak_ptrを通じて参照されるオブジェクト生きて、直接データ・アクセスを保持していないstd::weak_ptrできません。代わりに、参照されるオブジェクトへのstd::shared_ptr取得を試みるlock()メンバ関数を提供します:

#include <cassert>
#include <memory>
int main()
{
    {
         std::weak_ptr<int> wk;
         std::shared_ptr<int> sp;
         {
             std::shared_ptr<int> sh = std::make_shared<int>(42);
             wk = sh;
             // calling lock will create a shared_ptr to the object referenced by wk
             sp = wk.lock();
             // sh will be destroyed after this point, but sp is still alive
         }
         // sp still keeps the data alive.
         // At this point we could even call lock() again 
         // to retrieve another shared_ptr to the same data from wk
         assert(*sp == 42);
         assert(!wk.expired());
         // resetting sp will delete the data,
         // as it is currently the last shared_ptr with ownership
         sp.reset();
         // attempting to lock wk now will return an empty shared_ptr,
         // as the data has already been deleted
         sp = wk.lock();
         assert(!sp);
         assert(wk.expired());
     }
}

一意の所有権(std :: unique_ptr)

C ++ 11

std::unique_ptrは、動的に格納されたオブジェクトの存続期間を管理するクラステンプレートです。 std::shared_ptrとは異なり、動的オブジェクトはいつでもstd::unique_ptr 1つのインスタンスによってのみ所有され、


// Creates a dynamic int with value of 20 owned by a unique pointer
std::unique_ptr<int> ptr = std::make_unique<int>(20);

(注意: std::unique_ptrはC ++ 11以来、そしてstd::make_uniqueはC ++ 14以降で利用可能です。)

変数ptrだけが動的に割り当てられたintへのポインタを保持します。オブジェクトを所有する一意のポインタが有効範囲外になると、所有されているオブジェクトが削除されます。つまり、オブジェクトがクラス型で、そのオブジェクトのメモリが解放された場合、そのデストラクターが呼び出されます。

配列型でstd::unique_ptrstd::make_uniqueを使用するには、配列の特殊化を使用します。

// Creates a unique_ptr to an int with value 59
std::unique_ptr<int> ptr = std::make_unique<int>(59);

// Creates a unique_ptr to an array of 15 ints
std::unique_ptr<int[]> ptr = std::make_unique<int[]>(15);

これらの演算子はオーバーロードされているので、生ポインタと同様にstd::unique_ptrアクセスできます。


スマートポインタの内容の所有権を別のポインタにstd::movestd::moveを使用します。これにより、元のスマートポインタがnullptrを指すようになります。

// 1. std::unique_ptr
std::unique_ptr<int> ptr = std::make_unique<int>();

// Change value to 1
*ptr = 1;

// 2. std::unique_ptr (by moving 'ptr' to 'ptr2', 'ptr' doesn't own the object anymore)
std::unique_ptr<int> ptr2 = std::move(ptr);

int a = *ptr2; // 'a' is 1
int b = *ptr;  // undefined behavior! 'ptr' is 'nullptr'
               // (because of the move command above)

unique_ptrをパラメータとして関数に渡す:

void foo(std::unique_ptr<int> ptr)
{
    // Your code goes here
}

std::unique_ptr<int> ptr = std::make_unique<int>(59);
foo(std::move(ptr))

関数からunique_ptrを返します。これは、戻り値の所有権の意味を明確に伝えるので、ファクトリ関数を書くのに好ましいC ++ 11の方法です。呼び出し元はunique_ptrを所有し、その責任を負います。

std::unique_ptr<int> foo()
{
    std::unique_ptr<int> ptr = std::make_unique<int>(59);
    return ptr;
}

std::unique_ptr<int> ptr = foo();

これと比較する:

int* foo_cpp03();

int* p = foo_cpp03(); // do I own p? do I have to delete it at some point?
                      // it's not readily apparent what the answer is.
C ++ 14

クラステンプレートmake_uniqueは、C ++ 14以降で提供されています。手動でC ++ 11コードに手動で追加するのは簡単です:

template<typename T, typename... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args&&... args)
{ return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }

// Use make_unique for arrays
template<typename T>
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(size_t n)
{ return std::unique_ptr<T>(new typename std::remove_extent<T>::type[n]()); }
C ++ 11

ダムスマートポインタ( std::auto_ptr )とは異なり、 unique_ptrはベクトル割り当て( std::vectorではなく )でインスタンス化することもできます。これまでの例は、 スカラー割り当ての例でした。 10個の要素のための動的に割り当てられた整数配列を有するように、例えば、次のように指定しint[]テンプレートタイプ(だけではないようにint )。

std::unique_ptr<int[]> arr_ptr = std::make_unique<int[]>(10);

どちらを簡略化できるか:

auto arr_ptr = std::make_unique<int[]>(10);

さて、 arr_ptrを配列のように使用します:

arr_ptr[2] =  10; // Modify third element

割り当て解除について心配する必要はありません。このテンプレート特化バージョンは、コンストラクタとデストラクタを適切に呼び出します。ベクトル化されたバージョンのunique_ptrまたはvector自体を使用することは個人的な選択です。

C ++ 11より前のバージョンでは、 std::auto_ptrが利用可能でした。 unique_ptrとは異なり、 auto_ptrをコピーすることができます。このとき、ソースptrは含まれているポインタの所有権を失い、ターゲットはそれを受け取ります。

カスタム・リターを使用してCインターフェースへのラッパーを作成する

SDL2などの多くのCインタフェースには独自の削除機能があります。つまり、スマートポインタを直接使用することはできません。

std::unique_ptr<SDL_Surface> a; // won't work, UNSAFE!

代わりに、独自のデリゲーターを定義する必要があります。ここの例では、 SDL_Surface構造体を使用しています。これはSDL_FreeSurface()関数を使用して解放する必要がありますが、他の多くのCインタフェースにも適用可能です。

deleterはポインタ引数で呼び出し可能でなければならないため、例えば単純な関数ポインタにすることができます:

std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);

他の呼び出し可能なオブジェクトも動作します(たとえば、 operator()持つクラスなどoperator()

struct SurfaceDeleter {
    void operator()(SDL_Surface* surf) {
        SDL_FreeSurface(surf);
    }
};

std::unique_ptr<SDL_Surface, SurfaceDeleter> a(pointer, SurfaceDeleter{}); // safe
std::unique_ptr<SDL_Surface, SurfaceDeleter> b(pointer); // equivalent to the above
                                                         // as the deleter is value-initialized

これにより、安全でゼロのオーバーヘッド( unique_ptrを使用する場合)の自動メモリ管理が提供されるだけでなく、例外安全性も得られます。

Deleterはunique_ptrの型の一部であり、実装は空の基本最適化を使用して空のカスタムdeleterのサイズの変更を避けることができます。したがって、 std::unique_ptr<SDL_Surface, SurfaceDeleter>std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)>は同じ問題を同じように解決しますが、前者の型はポインタのサイズだけです。後者の型は2つのポインタを保持する必要があります: SDL_Surface*と関数ポインタ!フリー関数のカスタム・リターダを使用する場合は、空の型で関数をラップすることをお薦めします。

参照カウントが重要な場合は、 unique_ptr代わりにshared_ptrを使用できます。 shared_ptr常にDeleterが格納されています。これにより、APIで便利なDeleterの型が消去されます。 unique_ptrshared_ptrを使用することの欠点は、deleterを格納するためのメモリコストが高くなり、参照カウントを維持するためのパフォーマンスコストがかかることです。

// deleter required at construction time and is part of the type
std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);

// deleter is only required at construction time, not part of the type
std::shared_ptr<SDL_Surface> b(pointer, SDL_FreeSurface); 
C ++ 17

template auto 、カスタムレターを簡単にラップすることができます:

template <auto DeleteFn>
struct FunctionDeleter {
    template <class T>
    void operator()(T* ptr) {
        DeleteFn(ptr);
    }
};

template <class T, auto DeleteFn>
using unique_ptr_deleter = std::unique_ptr<T, FunctionDeleter<DeleteFn>>;

上記の例は単純に次のようになります。

unique_ptr_deleter<SDL_Surface, SDL_FreeSurface> c(pointer);

ここでautoの目的はvoid (例えばSDL_FreeSurface )を返してもしなくても( fclose )、すべての空き関数を処理することです。

移動セマンティクスのない独自の所有権(auto_ptr)

C ++ 11

注: std::auto_ptrはC ++ 11では廃止され、C ++では削除されます17。 C ++ 03以前のバージョンを使用することを余儀なくされ、気をつけたい場合にのみ、これを使用してください。 std::auto_ptr動作を置き換えるために、 std::moveと組み合わせてunique_ptrに移動することをお勧めします。

私たちがstd::unique_ptrを持つ前に、セマンティクスを移動させる前に、 std::auto_ptrを持っていました。 std::auto_ptrは一意の所有権を提供しますが、コピー時に所有権を転送します。

すべてのスマートポインタと同様に、 std::auto_ptr自動的にリソースをクリーンアップします( RAIIを参照)。

{
    std::auto_ptr<int> p(new int(42));
    std::cout << *p;
} // p is deleted here, no memory leaked

所有者は1人だけです:

std::auto_ptr<X> px = ...;
std::auto_ptr<X> py = px; 
  // px is now empty 

これにより、std :: auto_ptrを使用して所有権を明示的かつ一意に保つことができます。

void f(std::auto_ptr<X> ) {
    // assumes ownership of X
    // deletes it at end of scope
};

std::auto_ptr<X> px = ...;
f(px); // f acquires ownership of underlying X
       // px is now empty
px->foo(); // NPE!
// px.~auto_ptr() does NOT delete

所有権の移転は "コピー"コンストラクタで発生しました。 auto_ptrのコピーコンストラクタとコピー代入演算子は、オペランドを非const参照で取り出して変更できるようにします。実装例は次のとおりです。

template <typename T>
class auto_ptr {
    T* ptr;
public:
    auto_ptr(auto_ptr& rhs)
    : ptr(rhs.release())
    { }

    auto_ptr& operator=(auto_ptr& rhs) {
        reset(rhs.release());
        return *this;
    }

    T* release() {
        T* tmp = ptr;
        ptr = nullptr;
        return tmp;
    }

    void reset(T* tmp = nullptr) {
        if (ptr != tmp) {
            delete ptr;
            ptr = tmp;
        }
    }

    /* other functions ... */
};

これはコピーセマンティクスを壊します。コピーセマンティクスは、オブジェクトをコピーすると2つの同等のバージョンを残す必要があります。どのようなコピー可能なタイプのT 、私は次のように書くことができます:

T a = ...;
T b(a);
assert(b == a);

しかし、 auto_ptrの場合はそうではありません。その結果、 auto_ptrをコンテナに入れることは安全ではありません。

これを参照してshared_ptrを取得する

enable_shared_from_thisは、 thisに対して有効なshared_ptrインスタンスを取得できるようにします。

クラステンプレートenable_shared_from_thisからクラスを派生さshared_from_thisことで、 shared_ptrインスタンスをthis戻すshared_from_thisメソッドを継承します。

オブジェクトは最初にshared_ptrとして作成する必要があることに注意してください

#include <memory>
class A: public enable_shared_from_this<A> {
};
A* ap1 =new A();
shared_ptr<A> ap2(ap1); // First prepare a shared pointer to the object and hold it!
// Then get a shared pointer to the object from the object itself
shared_ptr<A> ap3 = ap1->shared_from_this(); 
int c3 =ap3.use_count(); // =2: pointing to the same object

(2)コンストラクタ内でenable_shared_from_thisを呼び出すことはできません。

#include <memory> // enable_shared_from_this

class Widget : public std::enable_shared_from_this< Widget >
{
public:
    void DoSomething()
    {
        std::shared_ptr< Widget > self = shared_from_this();
        someEvent -> Register( self );
    }
private:
    ...
};

int main()
{
    ...
    auto w = std::make_shared< Widget >();
    w -> DoSomething();
    ...
}

shared_ptrが所有していないオブジェクトshared_from_this()ローカルの自動オブジェクトやグローバルオブジェクトなどshared_from_this()shared_from_this()を使用すると、その動作は未定義です。 C ++ 17以降、代わりにstd::bad_allocスローされます。

コンストラクタからのshared_from_this()使用は、 shared_ptrが所有していないオブジェクトでの使用と同じです。これは、コンストラクタが返った後にshared_ptrがオブジェクトを所有しているためです。

キャストstd :: shared_ptrポインタ

直接使用することはできませんstatic_castconst_castdynamic_castし、 reinterpret_caststd::shared_ptr引数として渡されるポインタとポインタの共有の所有権を取得するために。代わりに、関数std::static_pointer_caststd::const_pointer_caststd::dynamic_pointer_castおよびstd::reinterpret_pointer_cast std::const_pointer_cast使用する必要があります。

struct Base { virtual ~Base() noexcept {}; };
struct Derived: Base {};
auto derivedPtr(std::make_shared<Derived>());
auto basePtr(std::static_pointer_cast<Base>(derivedPtr));
auto constBasePtr(std::const_pointer_cast<Base const>(basePtr));
auto constDerivedPtr(std::dynamic_pointer_cast<Derived const>(constBasePtr));

std::reinterpret_pointer_cast reinterpret_pointer_castはC ++ 11およびC ++ 14では使用できません.N3920によってのみ提案され、2014年2月にLibrary Fundamentals TS 採用されたためです。ただし、次のように実装できます。

template <typename To, typename From>
inline std::shared_ptr<To> reinterpret_pointer_cast(
    std::shared_ptr<From> const & ptr) noexcept
{ return std::shared_ptr<To>(ptr, reinterpret_cast<To *>(ptr.get())); }

スマートポインタの作成:value_ptr

value_ptrは、値のように動作するスマートポインタです。コピーすると、その内容がコピーされます。作成されると、その内容が作成されます。

// Like std::default_delete:
template<class T>
struct default_copier {
  // a copier must handle a null T const* in and return null:
  T* operator()(T const* tin)const {
    if (!tin) return nullptr;
    return new T(*tin);
  }
  void operator()(void* dest, T const* tin)const {
    if (!tin) return;
    return new(dest) T(*tin);
  }
};
// tag class to handle empty case:
struct empty_ptr_t {};
constexpr empty_ptr_t empty_ptr{};
// the value pointer type itself:
template<class T, class Copier=default_copier<T>, class Deleter=std::default_delete<T>,
  class Base=std::unique_ptr<T, Deleter>
>
struct value_ptr:Base, private Copier {
  using copier_type=Copier;
  // also typedefs from unique_ptr

  using Base::Base;

  value_ptr( T const& t ):
    Base( std::make_unique<T>(t) ),
    Copier()
  {}
  value_ptr( T && t ):
    Base( std::make_unique<T>(std::move(t)) ),
    Copier()
  {}
  // almost-never-empty:
      value_ptr():
    Base( std::make_unique<T>() ),
    Copier()
  {}
  value_ptr( empty_ptr_t ) {}

  value_ptr( Base b, Copier c={} ):
    Base(std::move(b)),
    Copier(std::move(c))
  {}

  Copier const& get_copier() const {
    return *this;
  }

  value_ptr clone() const {
    return {
      Base(
        get_copier()(this->get()),
        this->get_deleter()
      ),
      get_copier()
    };
  }
  value_ptr(value_ptr&&)=default;
  value_ptr& operator=(value_ptr&&)=default;

  value_ptr(value_ptr const& o):value_ptr(o.clone()) {}
  value_ptr& operator=(value_ptr const&o) {
    if (o && *this) {
      // if we are both non-null, assign contents:
      **this = *o;
    } else {
      // otherwise, assign a clone (which could itself be null):
      *this = o.clone();
    }
    return *this;
  }
  value_ptr& operator=( T const& t ) {
    if (*this) {
      **this = t;
    } else {
      *this = value_ptr(t);
    }
    return *this;
  }
  value_ptr& operator=( T && t ) {
    if (*this) {
      **this = std::move(t);
    } else {
      *this = value_ptr(std::move(t));
    }
    return *this;
  }
  T& get() { return **this; }
  T const& get() const { return **this; }
  T* get_pointer() {
    if (!*this) return nullptr;
    return std::addressof(get());
  }
  T const* get_pointer() const {
    if (!*this) return nullptr;
    return std::addressof(get());
  }
  // operator-> from unique_ptr
};
template<class T, class...Args>
value_ptr<T> make_value_ptr( Args&&... args ) {
  return {std::make_unique<T>(std::forward<Args>(args)...)};
}

この特定のvalue_ptrは、それをempty_ptr_t構築する場合、またはそこから移動する場合にのみ空です。それはそれがunique_ptrであるという事実を公開するので、 explicit operator bool() constがそれに作用する。 .get()はリファレンスを返すように変更されています(ほとんど空ではないため) .get_pointer()は代わりにポインタを返します。

このスマートポインタは、価値の意味を求めているpImpl場合には便利ですが、実装ファイルの外でpImplの内容を公開したくない場合もあります。

デフォルトではないCopierでは、派生したインスタンスを生成して値型に変換する方法を知っている仮想基本クラスも処理できます。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow