1. ホーム
  2. c++

[解決済み] shared_ptr<void>は合法で、unique_ptr<void>は不正な形式なのはなぜですか?

2022-09-25 12:41:53

疑問点

タイトルの通りの質問です。この違いの技術的な理由は何なのか、またその根拠は何なのか、知りたいと思っています。

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

どのように解決するのですか?

それは std::shared_ptr は型消去を実装しているのに対し std::unique_ptr はそうではありません。


から std::shared_ptr は型消去を実装しているので 別の という興味深い性質も持っています。 ではない は、削除者の型が必要である。 をテンプレート型引数として をクラステンプレートに渡す必要があります。それらの宣言を見てください。

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* , を格納することができますが、破壊時にオブジェクトを適切に削除することができます。 によって、正しく デストラクタを呼び出すことで .
  • deleterの型はクラステンプレートの型引数として渡されないので、少し自由です 型安全性を損なわずに .

さてさて。以上、どのように std::shared_ptr がどのように機能するかについてです。

さて、問題は std::unique_ptr オブジェクトを格納する として void* ? さて、その答えはというと です。 - ただし、引数として適切なデレッガを渡すことが条件です。以下はそのようなデモです。

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 番目のタイプのスマート ポインターのためのニッチ スポットが実際に存在することを意味します。

に対して スティーブ・ジェソップ は次のような解決策を提案しました。

実際に試したことはないのですが、もしかしたら適切な std::function をデレタ型として unique_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.

お役に立てれば幸いです。