std :: unique_ptr,deleters和Win32 API

时间:2013-02-12 20:40:42

标签: c++ winapi c++11 unique-ptr

在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:我需要了解一些转换魔法吗?

3 个答案:

答案 0 :(得分:46)

暂时忘掉自定义删除工具。当您说std::unique_ptr<T>时,unique_ptr构造函数希望收到T*,但CreateMutex会返回HANDLE,而不是HANDLE *

有三种方法可以解决这个问题:

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

答案 1 :(得分:29)

其他人已指出整个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返回HANDLE而不是HANDLE*,而不会有任何花哨的std::remove_pointer或其他此类内容。

这是有效的,因为HANDLE是一个指针,因此满足NullablePointer。

答案 2 :(得分:1)

问题是你实际上定义了unque_ptr来保存指向handle(HANDLE *)类型的指针,但你只传递HANDLE,而不是指向它的指针。