32

Windows API コードと相互運用するクラスを作成しています。現在、初期化する必要があるポインターの 1 つは、それを初期化するネイティブ関数を呼び出すことによって行われます。

私のポインターは、std::unique_ptr提供された WinAPI デリーター関数を呼び出すカスタム デリーターを備えたタイプですが、& address-of 演算子を使用して unique_ptr を init-function に渡すことはできません。なんで?

私の問題を示すサンプルを作成しました。

#include <memory>

struct foo
{
   int x;
};

struct custom_deleter {};

void init_foo(foo** init)
{
  *init = new foo();
}

int main()
{
   std::unique_ptr<foo, custom_deleter> foo_ptr;

   init_foo(&foo_ptr);
}

コンパイラは吠え、次のように言います。

source.cpp: In function 'int main()':
source.cpp:19:21: error: cannot convert 'std::unique_ptr<foo, custom_deleter>*' to 'foo**' for argument '1' to 'void init_foo(foo**)'
4

4 に答える 4

25

カバーの下のどこかに、unique_ptr<foo>タイプ のデータ メンバーがありfoo*ます。

ただし、クラスのユーザーがそのデータ メンバーを直接変更することは正当ではありません。これを行うと、 のクラス不変条件が保持されるとunique_ptrは限りません。特に、古いポインター値 (存在する場合) は解放されません。特別なケースでは、前の値が 0 であるため、発生する必要はありませんが、一般的には発生するはずです。

そのためunique_ptr、 はデータ メンバーへのアクセスを提供せず、その値のコピーへのアクセスのみを提供します ( および を介しget()operator->)。foo**から を取得することはできませんunique_ptr

代わりに次のように書くこともできます:

foo *tmp;
init_foo(&tmp);
std::unique_ptr<foo, custom_deleter> foo_ptr(tmp);

これは、例外セーフと同じ理由でstd::unique_ptr<foo, custom_deleter> foo_ptr(new foo());例外セーフですunique_ptr。コンストラクターに渡すものはすべて、最終的にデリータを使用して削除されることが保証されます。

ところで、?はcustom_deleter必要ありません。operator()(foo*)それとも私は何かを逃しましたか?

于 2012-09-13T10:13:18.510 に答える
7

スティーブはすでに技術的な問題が何であるかを説明しましたが、根本的な問題はもっと深いものです: コードはネイキッド ポインターを扱うときに役立つイディオムを採用しています。そもそも、なぜこのコードは 2 段階の初期化 (最初にオブジェクトを作成してから初期化する) を行うのでしょうか? スマート ポインターを使用したいので、コードを慎重に変更することをお勧めします。

foo* init_foo()
{
  return new foo();
}

int main()
{
   std::unique_ptr<foo, custom_deleter> foo_ptr( init_foo() );

}

もちろん、名前init_foo()を変更して aを直接create_foo()返す方がよいでしょう。std::unique_ptr<foo>また、2 段階の初期化を使用する場合は、多くの場合、クラスを使用してデータをラップすることを検討することをお勧めします。

于 2012-09-13T10:46:51.433 に答える
0

次のトリックを使用できます。

template<class T>
class ptr_setter
{
public:
    ptr_setter(T& Ptr): m_Ptr{Ptr} {}
    ~ptr_setter() { m_Ptr.reset(m_RawPtr); }

    ptr_setter(const ptr_setter&) = delete;
    ptr_setter& operator=(const ptr_setter&) = delete;

    auto operator&() { return &m_RawPtr; }

private:
    T& m_Ptr;
    typename T::pointer m_RawPtr{};
};


// Macro will not be needed with C++17 class template deduction.
// If you dislike macros (as all normal people should)
// it's possible to replace it with a helper function,
// although this would make the code a little more complex.

#define ptr_setter(ptr) ptr_setter<decltype(ptr)>(ptr)

その後:

std::unique_ptr<foo, custom_deleter> foo_ptr;
init_foo(&ptr_setter(foo_ptr));
于 2016-09-10T10:59:00.607 に答える