std::unique_ptr、删除器和 Win32 API

2021-12-17 00:00:00 winapi c++ c++11 unique-ptr

在 VC2012 中,我想使用唯一指针和删除器在构造函数中创建互斥锁,这样我就不需要创建析构函数来调用 CloseHandle.

In VC2012, I want to create a mutex in a constructor using a unique pointer and a deleter, so that I don't need to create a destructor just to call 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 *'

当我这样修改构造函数时:

When I modify the constructor thus:

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:是否有一些我需要了解的转换魔法?

I'm at a loss now. HANDLE is a typedef for void*: is there some conversion magic I need to know about?

推荐答案

暂时忘记自定义删除器.当你说 std::unique_ptr 时,unique_ptr 构造函数期望接收一个 T*,但 CreateMutex> 返回一个 HANDLE,而不是一个 HANDLE *.

Forget about the custom deleter for now. When you say std::unique_ptr<T>, the unique_ptr constructor expects to receive a T*, but CreateMutex returns a HANDLE, not a HANDLE *.

有 3 种方法可以解决此问题:

There are 3 ways to fix this:

std::unique_ptr<void, deleter> m_mutex;

您必须将 CreateMutex 的返回值转换为 void *.

You'll have to cast the return value of CreateMutex to a void *.

另一种方法是使用 std::remove_pointer 获取HANDLE 的底层类型.

Another way to do this is use std::remove_pointer to get to the HANDLE's underlying type.

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

另一种方法是利用以下事实:如果 unique_ptr 的删除器包含名为 pointer 的嵌套类型,则 unique_ptrcode> 将使用该类型作为其托管对象指针而不是 T*.

Yet another way to do this is to exploit the fact that if the unique_ptr's deleter contains a nested type named pointer, then the unique_ptr will use that type for its managed object pointer instead of 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时还需要注意创建函数指针时的调用约定.


Now, if you want to pass a pointer to function type as the deleter, then when dealing with the Windows API you also need to pay attention to the calling convention when creating function pointers.

所以,一个函数指针指向 CloseHandle 必须像这样

So, a function pointer to CloseHandle must look like this

BOOL(WINAPI *)(HANDLE)

综合起来,

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

我发现使用 lambda 更容易

I find it easier to use a lambda instead

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 来推断函数指针的类型.

Or as suggested by @hjmd in the comments, use decltype to deduce the type of the function pointer.

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

相关文章