サーチ…
構文
-
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
の私達のインスタンスの所有権を共有し、共有ポインタFoo
とfirstShared
。
スマートポインタは生ポインタと同じように機能します。つまり、 *
を使用して逆参照することができます。 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を使用して配列([])を割り当てる
残念ながら、 make_shared<>
を使用して配列を割り当てる直接的な方法はありません。
new
とstd::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では、 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);
p2
とp1
両方が型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)
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_ptr
とstd::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::move
、 std::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.
クラステンプレート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]()); }
ダムスマートポインタ( 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_ptr
にshared_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);
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)
注: 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_cast
、 const_cast
、 dynamic_cast
し、 reinterpret_cast
上std::shared_ptr
引数として渡されるポインタとポインタの共有の所有権を取得するために。代わりに、関数std::static_pointer_cast
、 std::const_pointer_cast
、 std::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
では、派生したインスタンスを生成して値型に変換する方法を知っている仮想基本クラスも処理できます。