2016-04-17 5 views
1

Я много работаю с Windows API. каждый раз, когда я получаю какой-то ресурс ОС (например, дескриптор ключа реестра, сокет и т. д.), я обматываю его вручную с помощью своего рода unique_ptr<HandleType,SomeDeleter>, чтобы иметь дело с правильным закрытием этого дескриптора.toUniquePtr реализация

Я пытаюсь создать функцию lightwight toUniquePtr, которая получает ручку и функцию закрытия и автоматически создает unique_ptr из двух.

для exmaple:

auto ptr = CreateFile(/**/); 
auto unique = toUniquePtr(ptr,&CloseHandle); 

Моя реализация до сих пор:

template <class T, class ClosingFunction> 
struct CostumeDeleter { 

    ClosingFunction closingFunction; 

    CostumeDeleter(ClosingFunction closingFunction_) : 
     closingFunction(closingFunction_) {} 

    CostumeDeleter(const CostumeDeleter&) = default; 
    CostumeDeleter(CostumeDeleter&&) = default; 

    void operator() (T t) { 
     closingFunction(t); 
    } 
}; 

template <class T, class F> 
inline auto toUniquePtr(T t, F f) { 
    CostumeDeleter<T, F> deleter(f); 
    std::unique_ptr<T, decltype(deleter)> pointer(t, deleter); 
    return std::move(pointer); 
} 

Пример использования + ошибки компиляции *:

void* handle = malloc(100); 
auto ptr = toUniquePtr(handle, &free); 

Я получаю ошибку от визуального студии 2015 RTM 1 :

Error C2664 'std::unique_ptr<T,CostumeDeleter<T,F>>::unique_ptr(const std::unique_ptr<T,CostumeDeleter<T,F>> &)': cannot convert argument 1 from 'void *' to 'void *' 

Что странно. любая помощь?

* Нет, я фактически не использую malloc в своей повседневной работе на C++, это просто для примера, поскольку его простая функция реплицирует ошибку компиляции.

+0

это звонит конструктор копирования для какой-либо причины. Конструктор копирования удаляется для 'unique_ptr'. –

+0

Вы не имели в виду 'inline auto toUniquePtr (T * t, F f) {', т.е. 'T' в настоящее время выводится как тип указателя –

+0

@PiotrSkotnicki, в любом случае это не имеет значения для компилятора, а некоторые ресурсы не являются типами указателей (например, SOCKET) и, во всяком случае, это не решило ошибку компиляции. –

ответ

4
template <class T, class F> 
inline auto toUniquePtr(T t, F f) 

Учитывая шаблон функции выше, T в настоящее время выводится как void *. Таким образом, std::unique_ptr<T, decltype(deleter)> рассчитывает пройти T *, aka void **.

Чтобы исправить ошибку, изменить шаблон

template <class T, class F> 
inline auto toUniquePtr(T *t, F f) 
//      ^^^^ 

Аналогично, изменить Deleter-х operator() принять T *.

Live demo


Другим решением является оставить все, как вы в настоящее время, и добавить следующий псевдоним определения DeleteR.

using pointer = T; 

Теперь unique_ptr будет управлять им <deleter_type>::pointer, т.е. void * вместо void **, и ваш код будет компилироваться.

Live demo

+0

Вы избили меня на пару секунд :( –

+0

Я пошел с 'using pointer = T', так как (как я прокомментировал) некоторые из ресурсов не являются указателями.Он работает красиво. Спасибо! –

+1

@DavidHaim Тип, unique_ptr' требуется для удовлетворения требований * NullablePointer *. Это означает, что вы не можете использовать его для управления ресурсом, который нельзя сравнивать с «nullptr», например. Но некоторые реализации stdlib по-прежнему позволяют вам делать это . [Этот пример] (http://coliru.stacked-crooked.com/a/443545bb28711275) принимается clang + libC++ и MSVC, последний работает, потому что они сравнивают ресурс со значением, инициализированным 'unique_ptr :: pointer', который является '0' в этом случае. libstdC++ отвергает его, потому что они сравнивают ресурс с' nullptr'. – Praetorian