Unique_ptrメンバーを持つクラスがあります。
class Foo {
private:
std::unique_ptr<Bar> bar;
...
};
バーは、create()関数とdestroy()関数を持つサードパーティクラスです。
スタンドアロン関数でstd::unique_ptr
を使用したい場合は、次のようにします。
void foo() {
std::unique_ptr<Bar, void(*)(Bar*)> bar(create(), [](Bar* b){ destroy(b); });
...
}
クラスのメンバーとしてstd::unique_ptr
を使用してこれを行う方法はありますか?
create
とdestroy
は、次のシグネチャを持つ無料の関数(OPのコードスニペットの場合のようです)であると仮定します。
Bar* create();
void destroy(Bar*);
このようにクラスFoo
を書くことができます
class Foo {
std::unique_ptr<Bar, void(*)(Bar*)> ptr_;
// ...
public:
Foo() : ptr_(create(), destroy) { /* ... */ }
// ...
};
destroy
はすでに削除プログラムであるため、ここでラムダまたはカスタムの削除プログラムを記述する必要はありません。
C++ 11のラムダ(G ++ 4.8.2でテスト済み)を使用して、これをきれいに行うことができます。
この再利用可能なtypedef
を考えると:
template<typename T>
using deleted_unique_ptr = std::unique_ptr<T,std::function<void(T*)>>;
あなたは書ける:
deleted_unique_ptr<Foo> foo(new Foo(), [](Foo* f) { customdeleter(f); });
たとえば、FILE*
の場合:
deleted_unique_ptr<FILE> file(
fopen("file.txt", "r"),
[](FILE* f) { fclose(f); });
これにより、try/catchノイズを必要とせずに、RAIIを使用した例外セーフクリーンアップの利点が得られます。
削除クラスを作成するだけです:
struct BarDeleter {
void operator()(Bar* b) { destroy(b); }
};
そして、unique_ptr
のテンプレート引数として提供します。コンストラクターでunique_ptrを初期化する必要があります。
class Foo {
public:
Foo() : bar(create()), ... { ... }
private:
std::unique_ptr<Bar, BarDeleter> bar;
...
};
私の知る限り、一般的なc ++ライブラリはすべてこれを正しく実装しています。 BarDeleter
には実際には状態がないため、unique_ptr
のスペースを占有する必要はありません。
実行時に削除者を変更できるようにする必要がない限り、カスタムの削除者タイプを使用することを強くお勧めします。たとえば、削除プログラムに関数ポインターを使用する場合は、sizeof(unique_ptr<T, fptr>) == 2 * sizeof(T*)
。つまり、unique_ptr
オブジェクトのバイトの半分が無駄になります。
ただし、すべての機能をラップするカスタム削除機能を作成するのは面倒です。ありがたいことに、関数にテンプレート化された型を書くことができます:
C++ 17以降:
template <auto fn>
using deleter_from_fn = std::integral_constant<decltype(fn), fn>;
template <typename T, auto fn>
using my_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;
// usage:
my_unique_ptr<Bar, destroy> p{create()};
C++ 17より前:
template <typename D, D fn>
using deleter_from_fn = std::integral_constant<D, fn>;
template <typename T, typename D, D fn>
using my_unique_ptr = std::unique_ptr<T, deleter_from_fn<D, fn>>;
// usage:
my_unique_ptr<Bar, decltype(destroy), destroy> p{create()};
ご存知のように、カスタム削除ツールを使用することは、コード全体に言及する必要があるため、最善の方法ではありません。
代わりに、 特殊化の追加が許可されている場合::std
の名前空間レベルのクラスに、カスタム型が関係し、セマンティクスを尊重する限り、次のようにします。
特化 std::default_delete
:
template <>
struct ::std::default_delete<Bar> {
default_delete() = default;
template <class U, class = std::enable_if_t<std::is_convertible<U*, Bar*>()>>
constexpr default_delete(default_delete<U>) noexcept {}
void operator()(Bar* p) const noexcept { destroy(p); }
};
そして多分 std::make_unique()
:
template <>
inline ::std::unique_ptr<Bar> ::std::make_unique<Bar>() {
auto p = create();
if (!p) throw std::runtime_error("Could not `create()` a new `Bar`.");
return { p };
}
Destroy関数でstd::bind
を使用するだけです。
std::unique_ptr<Bar, std::function<void(Bar*)>> bar(create(), std::bind(&destroy,
std::placeholders::_1));
しかしもちろん、ラムダを使用することもできます。
std::unique_ptr<Bar, std::function<void(Bar*)>> ptr(create(), [](Bar* b){ destroy(b);});