std::unique_ptr、删除器和 Win32 API

问题描述 投票:0回答:3

在 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* 的类型定义:我需要了解一些转换魔法吗?

c++ winapi c++11 unique-ptr
3个回答
49
投票

暂时忘记自定义删除器。当您说

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

35
投票

其他人指出了整个

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。


2
投票

问题是你实际上定义了 unque_ptr,它持有指向句柄 (HANDLE*) 类型的指针,但你传递的只是 HANDLE,而不是指向它的指针。

© www.soinside.com 2019 - 2024. All rights reserved.