この質問は本当にタイトルに当てはまります。私はこの違いの技術的理由は何かを知るのは興味深いですが、根拠もありますか?unique_ptr <void>は不正な形式であるのに対して、shared_ptrは<void>が合法なのはなぜですか?
std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
この質問は本当にタイトルに当てはまります。私はこの違いの技術的理由は何かを知るのは興味深いですが、根拠もありますか?unique_ptr <void>は不正な形式であるのに対して、shared_ptrは<void>が合法なのはなぜですか?
std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
std::unique_ptr
はそうではないstd::shared_ptr
は、型消去を実装しているからです。
std::shared_ptr
は型消去を実装しているので、それはまた別の興味深い特性、すなわちをサポートしています。それはではありませんは、クラステンプレートにテンプレートタイプ引数として、削除者のタイプを必要とします。彼らの宣言を見てください:
template<class T,class Deleter = std::default_delete<T> >
class unique_ptr;
template<class T>
class shared_ptr;
がそれを持っていない一方で、型パラメータとしてDeleter
を有しています。
ここで問題は、shared_ptr
は型消去を実装するのですか?さて、参照カウントをサポートする必要があり、これをサポートするために、ヒープからメモリを割り当てる必要があります。はに割り当てなければならないので、さらに1ステップ進んでタイプイレーズ—ヒープ割り当ても必要です。だから、基本的にはただの機会に過ぎない!そのため、型消去の
、std::shared_ptr
は2つのことをサポートすることができます:
void*
、として任意の型のオブジェクトを格納することができ、まだまだ正確で適切に破壊にをオブジェクトを削除することができますデストラクタを呼び出す。いいえ。これは、std::shared_ptr
の仕組みに関するすべてです。
ここで質問は、std::unique_ptr
オブジェクトを格納するをvoid*
とすることができますか?さて、答えははい —あなたが引数として適切なdeleterを渡すことを条件とします。ここではそのようなデモは次のとおりです。
int main()
{
auto deleter = [](void const * data) {
int const * p = static_cast<int const*>(data);
std::cout << *p << " located at " << p << " is being deleted";
delete p;
};
std::unique_ptr<void, decltype(deleter)> p(new int(959), deleter);
} //p will be deleted here, both p ;-)
出力(online demo):私は必要があります。私の場合は
:
959 located at 0x18aec20 is being deleted
あなたはコメントで非常に興味深い質問を型を削除する型ですが、(ヒープ割り当ての代償を払って)同様に可能であるようです。基本的には、これは実際にスマートポインタの第3のタイプのためのニッチスポットがあることを意味する:タイプの消去を有する排他的所有権スマートポインタ。
@Steve Jessopが、私は実際にこれを試したことがありませんが、多分あなたはunique_ptr
でデリータ型として適切なstd::function
を使用してそれを達成できるか。次のソリューション、
を提案しますかそれが実際に動作すれば、あなたは排他的所有権とタイプ消去されたDeleterを完了します。
はこの提案に続いて、私はこれを実装し、
using deleter_t = std::function<void(void *)>;
using unique_void_ptr = std::unique_ptr<void, deleter_t>;
template<typename T>
auto deleter(void const * data) -> void
{
T const * p = static_cast<T const*>(data);
std::cout << "{" << *p << "} located at [" << p << "] is being deleted.\n";
delete p;
}
template<typename T>
auto unique_void(T * ptr) -> unique_void_ptr
{
return unique_void_ptr(ptr, &deleter<T>);
}
int main()
{
auto p1 = unique_void(new int(959));
auto p2 = unique_void(new double(595.5));
auto p3 = unique_void(new std::string("Hello World"));
}
出力(online demo):
{Hello World} located at [0x2364c60] is being deleted.
{595.5} located at [0x2364c40] is being deleted.
{959} located at [0x2364c20] is being deleted.
希望は助けています。
1つの理由は、shared_ptr
の多くのユースケースの1つにあります。つまり、寿命指標またはセンチネルです。
これは、元のブーストドキュメントに記載された:
closure_target
は、このようなものである
auto register_callback(std::function<void()> closure, std::shared_ptr<void> pv)
{
auto closure_target = { closure, std::weak_ptr<void>(pv) };
...
// store the target somewhere, and later....
}
void call_closure(closure_target target)
{
// test whether target of the closure still exists
auto lock = target.sentinel.lock();
if (lock) {
// if so, call the closure
target.closure();
}
}
:
struct active_object : std::enable_shared_from_this<active_object>
{
void start() {
event_emitter_.register_callback([this] { this->on_callback(); },
shared_from_this());
}
void on_callback()
{
// this is only ever called if we still exist
}
};
:
struct closure_target {
std::function<void()> closure;
std::weak_ptr<void> sentinel;
};
呼び出し側は、このようなコールバックのものを登録しますshared_ptr<X>
は常にcoであるため
shared_ptr<void>
に変換可能であるため、event_emitterは、コールバックしているオブジェクトの種類をわかりやすく認識できます。
この構成では、交差するケースを処理する義務のイベントエミッタへの加入者を解放します(コールバックがactive_objectがなくなるのを待っていればどうでしょうか?)。また、同期する必要もない登録解除。 weak_ptr<void>::lock
は同期操作です。
いいえ、+1。しかし、 'std :: unique_ptr'が適切な 'D'を提供することによって依然として可能であることを明示的に述べることによって、より良いものにすることもできます。 –
Angew
@Angrew:いいです、私の質問に書かれていない本当の根本的な質問を見つけました;) –
@Nawaz:ありがとうございます。私の場合は、型を削除する型を必要としますが、(ヒープ割り当ての代償を払って)同様に可能であるようです。基本的には、これは実際にスマートポインタの第3のタイプのためのニッチスポットがあることを意味しますか?タイプ消去の排他的所有権スマートポインタ? –