Как использовать лямбду в качестве средства удаления std :: unique_ptr? - PullRequest
0 голосов
/ 05 сентября 2018

Проверьте следующую изобретенную программу:

#include <functional>
#include <memory>

template<typename T>
using UniPtr = std::unique_ptr<T, std::function<void(T*)>>;

int* alloc()
{
    return new int;
}

UniPtr<int> func()
{
    auto dealloc = [](int* p){delete p;};

    return UniPtr<int>{alloc(), dealloc};
}

int main()
{
    auto p = func();
    return 0;
}

Из руководства по построению std :: function , я думаю, создание объекта std::function может вызвать исключение, даже если отношение очень низкое:

UniPtr<int> func()
{
    auto dealloc = [](int* p){delete p;};

    return UniPtr<int>{alloc(), dealloc};
}

Но, если вместо std::function используется указатель на функцию:

template<typename T>
using UniPtr = std::unique_ptr<T, void(*)(T*)>;

Я думаю, что после выхода из области действия func() объект dealloc должен быть освобожден, и на него нельзя ссылаться. Пожалуйста, поправьте меня, если я ошибаюсь. Таким образом, единственный безопасный метод, который я могу предложить, - это определение глобальной dealloc функции:

void dealloc(int* p)
{
    delete p;
}

Но мне не нравится этот метод.

Исходя из изложенного выше, не существует 100% безопасного способа использования lambda в качестве std :: unique_ptr Deleter , или я что-то неправильно понимаю? Как использовать lambda как std::unique_ptr s Deleter?

Ответы [ 3 ]

0 голосов
/ 05 сентября 2018

Если вы определили UniPtr как

template<typename T>
using UniPtr = std::unique_ptr<T, void(*)(T*)>;

тогда следующий код действителен, нет никаких опасений по поводу времени жизни удалителя

UniPtr<int> func()
{
    auto dealloc = [](int* p){delete p;};
    return UniPtr<int>{alloc(), dealloc};
}

Цитата N3337, expr.prim.lambda / 6

Тип закрытия для лямбда-выражения без лямбда-захвата имеет публичную не виртуальную неявную const функцию преобразования в указатель на функцию, имеющую тот же параметр и возвращаемые типы в качестве оператора вызова функции типа замыкания. Значение, возвращаемое этой функцией преобразования, должно быть адресом функции , которая при вызове имеет тот же эффект, что и вызов оператора вызова функции типа замыкания.

Таким образом, ваш удалитель инициализируется указателем на функцию, которая остается в силе даже после возврата из func.

0 голосов
/ 05 сентября 2018

Небольшое дополнение для реализации к предыдущим ответам ...

template<typename T, typename D>
std::unique_ptr<T, D> make_unique_with_deleter(T* t, D d)
{
    // granted copy elison since C++17
    return std::unique_ptr<T, D> (t, d);
}

с помощью:

class A
{
};

auto up1 = make_unique_with_deleter(new A, [](A* a) {delete a; });
auto up2 = make_unique_with_deleter(std::fopen("something", "w"), std::fclose);

{
    int any_value = 0;
    // caution: only local use with lambda-capture, but possible
    auto up3 = make_unique_with_deleter(new A, [any_value](A* a) {delete a; });
}

Немного быстрое решение. Работает в разных сценариях. Он избегает использования в std: function с небольшими, но ненужными накладными расходами.

0 голосов
/ 05 сентября 2018

Я думаю, что после выхода из области действия func() объект dealloc должен быть освобожден, и на него нельзя ссылаться.

Тебе не нужно об этом беспокоиться. Да, лямбда-объект будет уничтожен, но указатель на функцию, возвращаемую лямбда-функцией преобразования указателя на функцию *1008*, всегда действителен, он не будет зависать.

Значение, возвращаемое этой функцией преобразования, является указателем на функцию со связью языка C ++, которая при вызове имеет тот же эффект, что и непосредственный вызов оператора вызова функции объекта замыкания.

...