26

VC2012では、一意のポインターとデリッターを使用してコンストラクターにミューテックスを作成したいので、CloseHandleを呼び出すためだけにデストラクタを作成する必要はありません。

私はこれがうまくいくと思っていたでしょう:

struct foo
{
    std::unique_ptr<HANDLE, BOOL(*)(HANDLE)> m_mutex;
    foo() : m_mutex(CreateMutex(NULL, FALSE, NULL), CloseHandle) {}
}

しかし、コンパイル時にエラーが発生します:

error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(void *,int 
(__cdecl *const &)(HANDLE)) throw()' : cannot convert parameter 1 from 
'HANDLE' to 'void *'

このようにコンストラクターを変更すると、次のようになります。

foo() : m_mutex((void*)CreateMutex(NULL, FALSE, 
    (name + " buffer mutex").c_str()), CloseHandle) {}

私はさらに珍しいことになります:

error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(void *,
int (__cdecl *const &)(HANDLE)) throw()' : cannot convert 
parameter 1 from 'void *' to 'void *'

私は今途方に暮れています。HANDLEはvoid*のtypedefです:知っておく必要のある変換の魔法はありますか?

4

3 に答える 3

48

今のところ、カスタム削除機能については忘れてください。と言うstd::unique_ptr<T>と、unique_ptrコンストラクターはを受け取ることを期待しますがT*、を返しますが、CreateMutexは返しHANDLEませんHANDLE *

これを修正する方法は3つあります。

std::unique_ptr<void, deleter> m_mutex;

の戻り値をにキャストする必要がありCreateMutexますvoid *

これを行う別の方法は、の基になる型std::remove_pointerに到達するために使用することです。HANDLE

std::unique_ptr<std::remove_pointer<HANDLE>::type, deleter> m_mutex;

これを行うさらに別の方法は、unique_ptr's deleterにという名前のネストされた型が含まれている場合pointerunique_ptrはその型を。の代わりにその管理対象オブジェクトポインタに使用するという事実を利用することですT*

struct mutex_deleter {
  void operator()( HANDLE h ) 
  {
    ::CloseHandle( h );
  }
  typedef HANDLE pointer;
};
std::unique_ptr<HANDLE, mutex_deleter> m_mutex;
foo() : m_mutex(::CreateMutex(NULL, FALSE, NULL), mutex_deleter()) {}

ここで、関数型へのポインターを削除ツールとして渡したい場合は、Windows APIを扱うときに、関数ポインターを作成するときの呼び出し規約にも注意を払う必要があります。

したがって、への関数ポインタは次のCloseHandleようになります。

BOOL(WINAPI *)(HANDLE)

すべてを組み合わせて、

std::unique_ptr<std::remove_pointer<HANDLE>::type, 
                BOOL(WINAPI *)(HANDLE)> m_mutex(::CreateMutex(NULL, FALSE, NULL),
                                                &::CloseHandle);

代わりにラムダを使用する方が簡単だと思います

std::unique_ptr<std::remove_pointer<HANDLE>::type, 
                void(*)( HANDLE )> m_mutex;
foo() : m_mutex(::CreateMutex(NULL, FALSE, NULL), 
                []( HANDLE h ) { ::CloseHandle( h ); }) {}

または、コメントで@hjmdが示唆しているように、を使用decltypeして関数ポインタの型を推測します。

std::unique_ptr<std::remove_pointer<HANDLE>::type, 
                decltype(&::CloseHandle)> m_mutex(::CreateMutex(NULL, FALSE, NULL),
                                                  &::CloseHandle);
于 2013-02-12T20:50:59.447 に答える
34

HANDLE他の人は、全体/HANDLE*問題がどのように機能するかを指摘しています。の興味深い機能を使用して、これに対処するためのはるかに賢い方法がありstd::unique_pointerます。

struct WndHandleDeleter
{
  typedef HANDLE pointer;

  void operator()(HANDLE h) {::CloseHandle(h);}
};

typedef std::unique_ptr<HANDLE, WndHandleDeleter> unique_handle;

これにより、派手なことなどをせずに、の代わりunique_handle::getに戻ることができます。HANDLEHANDLE*std::remove_pointer

これHANDLEは、がポインタであり、したがってNullablePointerを満たすために機能します。

于 2013-02-12T21:20:17.157 に答える
1

問題は、ハンドル(HANDLE *)型へのポインターを保持するunque_ptrを実際に定義しているのに、ポインターではなくHANDLEだけを渡すことです。

于 2013-02-12T20:51:25.160 に答える