c++ std::unique_ptr、删除器和Win32 API

nafvub8i  于 2023-02-26  发布在  其他
关注(0)|答案(3)|浏览(163)

在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* 的类型定义:有什么转化魔法需要我知道吗?

wtlkbnrh

wtlkbnrh1#

现在先不考虑自定义删除器,当您使用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的删除器包含名为pointer的嵌套类型,则unique_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);

我发现使用lambda更容易

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

zbdgwd5y2#

其他人已经指出了整个HANDLE/HANDLE*问题是如何工作的,下面是一个更聪明的方法来处理它,使用std::unique_ptr有趣的特性。

struct WndHandleDeleter
{
  typedef HANDLE pointer;

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

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

这允许unique_handle::get返回HANDLE而不是HANDLE*,而不需要任何花哨的std::remove_pointer或其他类似的东西。
这是因为HANDLE是一个指针,因此满足NullablePointer。

62lalag4

62lalag43#

问题是您实际上定义了unque_ptr来保存指向句柄(HANDLE*)类型的指针,但是您只传递了HANDLE,而没有传递指向它的指针。

相关问题