unique_ptr <void>の形式が正しくないのに、shared_ptr <void>が合法であるのはなぜですか?


100

質問はタイトルに本当に当てはまります。この違いの技術的な理由は何ですか、そしてその根拠も知りたいです。

std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;

回答:


118

これは、std::shared_ptr型消去を実装しているのに対し、実装してstd::unique_ptrいないためです。


std::shared_ptr型消去を実装しているため、別の興味深いプロパティ、つまりviz もサポートしています。クラステンプレートへのテンプレートタイプ引数として、削除機能のタイプは必要ありませ。彼らの宣言を見てください:

template<class T,class Deleter = std::default_delete<T> > 
class unique_ptr;

これは持っていDeleterながら、型パラメータとして

template<class T> 
class shared_ptr;

それはありません。

今問題は、なぜshared_ptr型消去を実装するのですか?参照カウントをサポートする必要があるため、そうします。これをサポートするには、ヒープからメモリ割り当てる必要があります。とにかくメモリ割り当てる必要があるため、さらに一歩進んで、型消去(ヒープが必要)を実装します。割り当ても。つまり、基本的には日和見主義者です。

型消去のため、std::shared_ptrは次の2つをサポートできます。

  • それはのように任意の型のオブジェクトを格納することができvoid*まだまだ適切に破壊上のオブジェクトを削除することができ、正しくによって彼らのデストラクタを呼び出します
  • 削除者のタイプは、クラステンプレートにタイプ引数として渡されません。つまり、タイプセーフを損なうことなく少し自由度があります。

よし。それがどのようにstd::shared_ptr機能するかについてのすべてです。

今問題は、std::unique_ptrオブジェクト void*?ええ、答えは、はいです。適切な削除プログラムを引数として渡した場合です。そのようなデモの1つを次に示します。

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 ;-)

出力(オンラインデモ):

959 located at 0x18aec20 is being deleted

あなたはコメントで非常に興味深い質問をしました:

私の場合、型を削除する削除機能が必要になりますが、それも可能です(ヒープ割り当てを犠牲にして)。基本的に、これは実際には3番目のタイプのスマートポインターのニッチスポットがあることを意味しますか?タイプ消去付きの排他的所有権スマートポインターです。

これに@Steveジェソップは、以下のソリューションを提案し、

私は実際にこれを試したことはありませんが、おそらくstd::functionunique_ptr?それが実際に機能するとしたら、あなたは完了し、独占的な所有権と型消去された削除プログラムを実行します。

この提案に従って、私はこれを実装しました(ただし、std::function必要と思われないため、これを利用していません)。

using unique_void_ptr = std::unique_ptr<void, void(*)(void const*)>;

template<typename T>
auto unique_void(T * ptr) -> unique_void_ptr
{
    return unique_void_ptr(ptr, [](void const * data) {
         T const * p = static_cast<T const*>(data);
         std::cout << "{" << *p << "} located at [" << p <<  "] is being deleted.\n";
         delete p;
    });
}

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"));
}  

出力(オンラインデモ):

{Hello World} located at [0x2364c60] is being deleted.
{595.5} located at [0x2364c40] is being deleted.
{959} located at [0x2364c20] is being deleted.

お役に立てば幸いです。


13
良い答え、+ 1。しかしstd::unique_ptr<void, D>、適切なを提供することでa がまだ可能であることを明示的に言及することで、それをさらに改善することができDます。
AngewはSO

1
@Angrew:いい質問です。私の質問に書かれていない根本的な質問を見つけました;)
Ad N

@Nawaz:ありがとう。私の場合、型を削除する削除プログラムが必要になりますが、それも可能です(ヒープの割り当てを犠牲にして)。基本的に、これは実際には3番目のタイプのスマートポインターのニッチスポットがあることを意味しますか?タイプの消去を備えた独占所有権スマートポインター?
広告N

8
@AdN:私は実際にこれを試したことはありませんがstd::function、デリケーターのタイプとして適切なものを使用することでそれを達成できunique_ptrますか?それが実際に機能するとしたら、あなたは完了し、独占的な所有権と型消去された削除プログラムを実行します。
Steve Jessop

文法nit:「なぜX動詞Yなのか」「なぜする必要がありません X 動詞 Yは?」英語で。
zwol 2016

7

理論的根拠の1つは、aの多くのユースケースの1つにありますshared_ptr-つまり、寿命インジケーターまたはセンチネルとして。

これは元のブーストのドキュメントで言及されていました:

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();
    }
}

closure_targetこのようなものはどこにありますか:

struct closure_target {
    std::function<void()> closure;
    std::weak_ptr<void> sentinel;
};

呼び出し元は、次のようなコールバックを登録します。

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 
    }
};

ので、shared_ptr<X>常にに変換されshared_ptr<void>、event_emitterは今、それがバックに呼び出しているオブジェクトの種類の穏やか気づいていないことができます。

この配置により、サブスクライバーがイベントエミッターのクロスケースを処理する義務が解放され(コールバックがキューにある場合、active_objectがなくなるまでアクションが実行されるのを待機していますか?)、サブスクリプションを同期する必要がないことも意味します。weak_ptr<void>::lock同期操作です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.