22

std::unique_ptrs を使用して Windows ハンドルを例外セーフな方法で管理しようとしています。

最初に試しました:

struct HandleDeleter
{
    void operator()( HANDLE handle )
    {
        if( handle )
        {
            FindVolumeClose( handle )
        }
    }
}
typedef std::unique_ptr< HANDLE, HandleDeleter > unique_vol_handle_t;

後でコードを使用しようとすると、次のようになります。

unique_vol_handle_t volH( FindFirstVolumeW( buffer, MAX_GUID_PATH ) );

Visual Studio 2012RC から次のエラーが表示されます。

1>          error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(std::nullptr_t) throw()' : cannot convert parameter 1 from 'HANDLE' to 'std::nullptr_t'
1>          with
1>          [
1>              _Ty=HANDLE,
1>              _Dx=VolumeHandleDeleter
1>          ]
1>          nullptr can only be converted to pointer or handle types

すぐ上の volH 宣言行を参照します。

しばらく検索した後、基本的に次のように述べているブログ記事を見つけました。

typedef HANDLE pointer;

構造体宣言の先頭に追加すると、すべてがうまくいきます。

信じられませんでしたが、試してみたところ、エラーは解決しました。型を (型を参照することさえせずに) 定義することで、どのようにこのような違いが生じるのか、私は困惑しています。

2 つの質問:

1) 元のエラーを説明できますか? コンパイラが を参照している理由がわかりませんstd::nullptr_t/nullptr

2) typedef がこれをどのように解決しますか (または少なくともそう思われるか)? これに対する「遠く離れた不気味なアクション」の解決策はありますか?

4

7 に答える 7

26

unique_ptrデリータに型が存在するかどうかのチェックの実装::pointer。デリータに::pointer型がある場合、この型は のpointertypedefとして使用されますunique_ptr。それ以外の場合は、最初のテンプレート引数へのポインターが使用されます。

cppreference.comによると、unique_ptr::pointer型は次のように定義されています。

std::remove_reference<D>::type::pointerそのタイプが存在する場合、そうでない場合T*

于 2012-08-29T19:14:35.893 に答える
6

unique_ptrのMSDNマニュアルから:

所有されているリソースへの格納されたポインタは、stored_ptr型ポインタを持っています。Del::pointer定義されている場合とType *そうでない場合です。削除者がステートレスの場合、格納された削除オブジェクトstored_deleterはオブジェクト内のスペースを占有しません。Delは参照型である可能性があることに注意してください。

これは、デリータファンクタを提供する場合pointer、の実際のポインタタイプに使用されるタイプを提供する必要があることを意味しますunique_ptrHANDLE*そうでなければ、それはあなたの場合、正しくないあなたの提供されたタイプへのポインタになります。

于 2012-08-29T19:17:28.903 に答える
5

私は、Windows のさまざまな種類のハンドルに対して次のことを行ってきました。どこかで次のように宣言したとします。

std::unique_ptr<void, decltype (&FindVolumeClose)> fv (nullptr, FindVolumeClose);

これには以下が含まれます。

HANDLE temp = FindFirstVolume (...);
if (temp != INVALID_HANDLE_VALUE)
    fv.reset (temp);

デリーターをラップするために別の構造体を宣言する必要はありません。HANDLEは実際にはvoid *そのunique_ptrタイプvoidとして取得されるためです。マクロを使用する他の種類のハンドルの場合DECLARE_HANDLE、これは回避できます。

// Manages the life of a HHOOK
std::unique_ptr<HHOOK__, decltype (&UnhookWindowsHookEx)> hook (nullptr, UnhookWindowsHookEx);

等々。

于 2013-06-21T20:41:56.463 に答える
3

A Proposal to Add additional RAII Wrappers to the Standard Libraryと、ハンドル付きのスマート ポインターを使用することの欠点を確認することをお勧めします。

std::unique_ptr個人的には、これらの状況で使用する代わりに、その提案の参照実装を使用しています。

于 2013-02-15T23:44:39.637 に答える